package trader

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"log"
	"math"
	nofxconfig "nofx/config"
	"nofx/decision"
	"nofx/logger"
	"nofx/market"
	"nofx/martingale"
	"nofx/mcp"
	"nofx/pool"
	"strconv"
	"strings"
	"sync"
	"time"
)

const (
	takerFeeRate        = 0.0004
	maxMarginUsageRatio = 0.995 // 预留少量余额，避免因费用波动触发-2019
)

// AutoTraderConfig 自动交易配置（简化版 - AI全权决策）
type AutoTraderConfig struct {
	// Trader标识
	ID      string // Trader唯一标识（用于日志目录等）
	Name    string // Trader显示名称
	AIModel string // AI模型: "qwen" 或 "deepseek"

	// 交易平台选择
	Exchange string // "binance", "hyperliquid" 或 "aster"

	// 币安API配置
	BinanceAPIKey    string
	BinanceSecretKey string

	// Hyperliquid配置
	HyperliquidPrivateKey string
	HyperliquidWalletAddr string
	HyperliquidTestnet    bool

	// Aster配置
	AsterUser       string // Aster主钱包地址
	AsterSigner     string // Aster API钱包地址
	AsterPrivateKey string // Aster API钱包私钥

	CoinPoolAPIURL string

	// AI配置
	UseQwen     bool
	DeepSeekKey string
	QwenKey     string

	// 自定义AI API配置
	CustomAPIURL    string
	CustomAPIKey    string
	CustomModelName string

	// 扫描配置
	ScanInterval time.Duration // 扫描间隔（建议5分钟）

	// 账户配置
	InitialBalance float64 // 初始金额（用于计算盈亏，需手动设置）

	// 杠杆配置
	BTCETHLeverage  int // BTC和ETH的杠杆倍数
	AltcoinLeverage int // 山寨币的杠杆倍数

	// 风险控制（仅作为提示，AI可自主决定）
	MaxDailyLoss    float64       // 最大日亏损百分比（提示）
	MaxDrawdown     float64       // 最大回撤百分比（提示）
	StopTradingTime time.Duration // 触发风控后暂停时长

	// 仓位模式
	IsCrossMargin bool // true=全仓模式, false=逐仓模式

	// 币种配置
	DefaultCoins []string // 默认币种列表（从数据库获取）
	TradingCoins []string // 实际交易币种列表

	// 系统提示词模板
	SystemPromptTemplate string // 系统提示词模板名称（如 "default", "aggressive"）
}

// AutoTrader 自动交易器
type AutoTrader struct {
	id                    string // Trader唯一标识
	name                  string // Trader显示名称
	aiModel               string // AI模型名称
	exchange              string // 交易平台名称
	config                AutoTraderConfig
	trader                Trader // 使用Trader接口（支持多平台）
	mcpClient             mcp.AIClient
	decisionLogger        logger.IDecisionLogger // 决策日志记录器
	initialBalance        float64
	dailyPnL              float64
	customPrompt          string   // 自定义交易策略prompt
	overrideBasePrompt    bool     // 是否覆盖基础prompt
	systemPromptTemplate  string   // 系统提示词模板名称
	defaultCoins          []string // 默认币种列表（从数据库获取）
	tradingCoins          []string // 实际交易币种列表
	lastResetTime         time.Time
	stopUntil             time.Time
	isRunning             bool
	startTime             time.Time                        // 系统启动时间
	callCount             int                              // AI调用次数
	statusMutex           sync.RWMutex                     // 保护 isRunning, startTime, callCount 的并发访问
	positionFirstSeenTime map[string]int64                 // 持仓首次出现时间 (symbol_side -> timestamp毫秒)
	lastPositions         map[string]decision.PositionInfo // 上一次周期的持仓快照 (用于检测被动平仓)
	positionStopLoss      map[string]float64               // 持仓止损价格 (symbol_side -> stop_loss_price)
	positionTakeProfit    map[string]float64               // 内存中缓存的止盈价格 (key: symbol_side)
	stopMonitorCh         chan struct{}
	monitorWg             sync.WaitGroup
	peakPnLCache          map[string]float64  // 最高收益缓存 (key: symbol_side)
	peakPnLCacheMutex     sync.RWMutex        // 保护最高收益缓存的读写锁
	lastBalanceSyncTime   time.Time           // 上次余额同步时间
	database              interface{}         // 数据库引用（用于自动更新余额）
	userID                string              // 用户ID
	martingaleManager     *martingale.Manager // 马丁格尔管理器
}

// providerDisplayNames AI provider 显示名称映射
var providerDisplayNames = map[string]string{
	"custom":    "自定义AI API",
	"openai":    "OpenAI API",
	"anthropic": "Anthropic Claude API",
	"gemini":    "Gemini API",
	"groq":      "Groq API",
	"grok":      "Grok API",
	"qwen":      "阿里云Qwen AI",
	"deepseek":  "DeepSeek AI",
}

// initMCPClient 初始化 AI MCP 客户端
// 根据 config.AIModel 选择对应的 provider 并设置 API key
func initMCPClient(config AutoTraderConfig) mcp.AIClient {
	mcpClient := mcp.New()
	provider := config.AIModel

	// 使用 CustomAPIKey 的 provider（统一处理）
	customKeyProviders := map[string]bool{
		"custom": true, "openai": true, "anthropic": true,
		"gemini": true, "groq": true, "grok": true,
	}

	if customKeyProviders[provider] {
		mcpClient.SetAPIKey(config.CustomAPIKey, config.CustomAPIURL, config.CustomModelName, provider)
		displayName := providerDisplayNames[provider]
		if provider == "custom" {
			log.Printf("🤖 [%s] 使用%s: %s (模型: %s)", config.Name, displayName, config.CustomAPIURL, config.CustomModelName)
		} else {
			log.Printf("🤖 [%s] 使用 %s (模型: %s)", config.Name, displayName, config.CustomModelName)
		}
		return mcpClient
	}

	// Qwen 使用专用 key
	if provider == "qwen" || (provider == "" && config.UseQwen) {
		mcpClient = mcp.NewQwenClient()
		mcpClient.SetAPIKey(config.QwenKey, config.CustomAPIURL, config.CustomModelName, "qwen")
		log.Printf("🤖 [%s] 使用阿里云Qwen AI", config.Name)
		return mcpClient
	}

	// 默认: DeepSeek
	mcpClient = mcp.NewDeepSeekClient()
	mcpClient.SetAPIKey(config.DeepSeekKey, config.CustomAPIURL, config.CustomModelName, "deepseek")
	log.Printf("🤖 [%s] 使用DeepSeek AI", config.Name)
	return mcpClient
}

// NewAutoTrader 创建自动交易器
func NewAutoTrader(config AutoTraderConfig, database interface{}, userID string) (*AutoTrader, error) {
	// 设置默认值
	if config.ID == "" {
		config.ID = "default_trader"
	}
	if config.Name == "" {
		config.Name = "Default Trader"
	}
	if config.AIModel == "" {
		if config.UseQwen {
			config.AIModel = "qwen"
		} else {
			config.AIModel = "deepseek"
		}
	}

	mcpClient := initMCPClient(config)

	// 从数据库读取 AI Temperature 配置
	if database != nil {
		if db, ok := database.(*nofxconfig.Database); ok && db != nil {
			if tempStr, err := db.GetSystemConfig("ai_temperature"); err == nil && tempStr != "" {
				if temp, err := strconv.ParseFloat(tempStr, 64); err == nil && temp >= 0 && temp <= 1 {
					mcpClient.SetTemperature(temp)
					log.Printf("🌡️  [%s] AI Temperature: %.2f", config.Name, temp)
				}
			}
		}
	}

	// 初始化币种池API
	if config.CoinPoolAPIURL != "" {
		pool.SetCoinPoolAPI(config.CoinPoolAPIURL)
	}

	// 设置默认交易平台
	if config.Exchange == "" {
		config.Exchange = "binance"
	}

	// 根据配置创建对应的交易器
	var trader Trader
	var err error

	// 记录仓位模式（通用）
	marginModeStr := "全仓"
	if !config.IsCrossMargin {
		marginModeStr = "逐仓"
	}
	log.Printf("📊 [%s] 仓位模式: %s", config.Name, marginModeStr)

	switch config.Exchange {
	case "binance":
		log.Printf("🏦 [%s] 使用币安合约交易", config.Name)
		trader = NewFuturesTrader(config.BinanceAPIKey, config.BinanceSecretKey, userID)
	case "hyperliquid":
		log.Printf("🏦 [%s] 使用Hyperliquid交易", config.Name)
		trader, err = NewHyperliquidTrader(config.HyperliquidPrivateKey, config.HyperliquidWalletAddr, config.HyperliquidTestnet)
		if err != nil {
			return nil, fmt.Errorf("初始化Hyperliquid交易器失败: %w", err)
		}
	case "aster":
		log.Printf("🏦 [%s] 使用Aster交易", config.Name)
		trader, err = NewAsterTrader(config.AsterUser, config.AsterSigner, config.AsterPrivateKey)
		if err != nil {
			return nil, fmt.Errorf("初始化Aster交易器失败: %w", err)
		}
	default:
		return nil, fmt.Errorf("不支持的交易平台: %s", config.Exchange)
	}

	// 验证初始金额配置
	if config.InitialBalance <= 0 {
		return nil, fmt.Errorf("初始金额必须大于0，请在配置中设置InitialBalance")
	}

	// 初始化决策日志记录器（使用trader ID创建独立目录）
	logDir := fmt.Sprintf("decision_logs/%s", config.ID)
	decisionLogger := logger.NewDecisionLogger(logDir)

	// 设置默认系统提示词模板
	systemPromptTemplate := config.SystemPromptTemplate
	if systemPromptTemplate == "" {
		// feature/partial-close-dynamic-tpsl 分支默认使用 adaptive（支持动态止盈止损）
		systemPromptTemplate = "adaptive"
	}

	var sqlDB *sql.DB
	if db, ok := database.(*nofxconfig.Database); ok && db != nil {
		sqlDB = db.Conn()
	}
	martingaleManager := martingale.NewManager(sqlDB)
	if sqlDB != nil {
		if err := martingaleManager.LoadPlans(); err != nil {
			log.Printf("⚠️ 加载马丁格尔计划失败: %v", err)
		} else {
			log.Printf("✅ 已加载马丁格尔计划")
		}
	}

	return &AutoTrader{
		id:                    config.ID,
		name:                  config.Name,
		aiModel:               config.AIModel,
		exchange:              config.Exchange,
		config:                config,
		trader:                trader,
		mcpClient:             mcpClient,
		decisionLogger:        decisionLogger,
		initialBalance:        config.InitialBalance,
		systemPromptTemplate:  systemPromptTemplate,
		defaultCoins:          config.DefaultCoins,
		tradingCoins:          config.TradingCoins,
		lastResetTime:         time.Now(),
		startTime:             time.Now(),
		callCount:             0,
		isRunning:             false,
		positionFirstSeenTime: make(map[string]int64),
		lastPositions:         make(map[string]decision.PositionInfo),
		positionStopLoss:      make(map[string]float64),
		positionTakeProfit:    make(map[string]float64),
		stopMonitorCh:         make(chan struct{}),
		monitorWg:             sync.WaitGroup{},
		peakPnLCache:          make(map[string]float64),
		peakPnLCacheMutex:     sync.RWMutex{},
		lastBalanceSyncTime:   time.Now(), // 初始化为当前时间
		database:              database,
		userID:                userID,
		martingaleManager:     martingaleManager,
	}, nil
}

// waitUntilNextInterval 等待到下一个扫描间隔的整点时间
// 确保扫描时间与K线周期对齐，获取完整的K线数据
// 返回 true 表示正常完成等待，false 表示被停止信号中断
func (at *AutoTrader) waitUntilNextInterval() bool {
	interval := at.config.ScanInterval
	now := time.Now()

	// 计算下一个整点时间（例如：扫描间隔5分钟，则等待到 00:00, 00:05, 00:10...）
	next := now.Truncate(interval).Add(interval)

	// 额外延迟5秒，确保交易所K线数据已更新
	delay := next.Sub(now) + 5*time.Second

	log.Printf("⏰ 等待到下一个整点时间: %s (延迟 %v)", next.Format("15:04:05"), delay.Round(time.Second))

	// 使用 select 使等待可中断，避免 Stop() 后仍执行交易
	select {
	case <-time.After(delay):
		log.Printf("✅ 已对齐到整点，开始扫描")
		return true
	case <-at.stopMonitorCh:
		log.Printf("⏹ 等待期间收到停止信号，取消启动")
		return false
	}
}

// Run 运行自动交易主循环
func (at *AutoTrader) Run() error {
	at.statusMutex.Lock()
	at.isRunning = true
	at.stopMonitorCh = make(chan struct{})
	at.startTime = time.Now()
	at.statusMutex.Unlock()

	log.Println("🚀 AI驱动自动交易系统启动")
	log.Printf("💰 初始余额: %.2f USDT", at.initialBalance)
	log.Printf("⚙️  扫描间隔: %v", at.config.ScanInterval)
	log.Println("🤖 AI将全权决定杠杆、仓位大小、止损止盈等参数")
	at.monitorWg.Add(1)
	defer at.monitorWg.Done()

	// 启动回撤监控
	at.startDrawdownMonitor()

	// 启动马丁格尔独立轮询
	go at.runMartingaleLoop()

	// 等待到下一个整点时间，确保K线数据完整
	if !at.waitUntilNextInterval() {
		return nil // 等待期间被停止
	}

	// 再次检查是否仍在运行（防止等待完成后刚好被停止）
	if !at.IsRunning() {
		return nil
	}

	ticker := time.NewTicker(at.config.ScanInterval)
	defer ticker.Stop()

	// 首次执行（已对齐到整点）
	if err := at.runCycle(); err != nil {
		log.Printf("❌ 执行失败: %v", err)
	}

	for at.IsRunning() {
		select {
		case <-ticker.C:
			if err := at.runCycle(); err != nil {
				log.Printf("❌ 执行失败: %v", err)
			}
		case <-at.stopMonitorCh:
			log.Printf("[%s] ⏹ 收到停止信号，退出自动交易主循环", at.name)
			return nil
		}
	}

	return nil
}

// Stop 停止自动交易
func (at *AutoTrader) Stop() {
	at.statusMutex.Lock()
	if !at.isRunning {
		at.statusMutex.Unlock()
		return
	}
	at.isRunning = false
	at.statusMutex.Unlock()
	close(at.stopMonitorCh) // 通知监控goroutine停止
	at.monitorWg.Wait()     // 等待监控goroutine结束
	log.Println("⏹ 自动交易系统停止")
}

// IsRunning 返回当前运行状态（线程安全）
func (at *AutoTrader) IsRunning() bool {
	at.statusMutex.RLock()
	defer at.statusMutex.RUnlock()
	return at.isRunning
}

// runCycle 运行一个交易周期（使用AI全权决策）
func (at *AutoTrader) runCycle() error {
	at.statusMutex.Lock()
	at.callCount++
	at.statusMutex.Unlock()

	log.Print("\n" + strings.Repeat("=", 70) + "\n")
	log.Printf("⏰ %s - AI决策周期 #%d", time.Now().Format("2006-01-02 15:04:05"), at.callCount)
	log.Println(strings.Repeat("=", 70))

	// 创建决策记录
	record := &logger.DecisionRecord{
		Exchange:     at.config.Exchange, // 记录交易所类型，用于计算手续费
		ExecutionLog: []string{},
		Success:      true,
	}

	// 1. 检查是否需要停止交易
	if time.Now().Before(at.stopUntil) {
		remaining := time.Until(at.stopUntil)
		log.Printf("⏸ 风险控制：暂停交易中，剩余 %.0f 分钟", remaining.Minutes())
		record.Success = false
		record.ErrorMessage = fmt.Sprintf("风险控制暂停中，剩余 %.0f 分钟", remaining.Minutes())
		at.decisionLogger.LogDecision(record)
		return nil
	}

	// 2. 重置日盈亏（每天重置）
	if time.Since(at.lastResetTime) > 24*time.Hour {
		at.dailyPnL = 0
		at.lastResetTime = time.Now()
		log.Println("📅 日盈亏已重置")
	}

	// 3. 马丁格尔策略已移至独立轮询 (runMartingaleLoop)

	// 4. 收集交易上下文
	ctx, err := at.buildTradingContext()
	if err != nil {
		record.Success = false
		record.ErrorMessage = fmt.Sprintf("构建交易上下文失败: %v", err)
		at.decisionLogger.LogDecision(record)
		return fmt.Errorf("构建交易上下文失败: %w", err)
	}

	// 保存账户状态快照
	record.AccountState = logger.AccountSnapshot{
		TotalBalance:          ctx.Account.TotalEquity - ctx.Account.UnrealizedPnL,
		AvailableBalance:      ctx.Account.AvailableBalance,
		TotalUnrealizedProfit: ctx.Account.UnrealizedPnL,
		PositionCount:         ctx.Account.PositionCount,
		MarginUsedPct:         ctx.Account.MarginUsedPct,
		InitialBalance:        at.initialBalance, // 记录当时的初始余额基准
	}

	// 保存持仓快照
	for _, pos := range ctx.Positions {
		record.Positions = append(record.Positions, logger.PositionSnapshot{
			Symbol:           pos.Symbol,
			Side:             pos.Side,
			PositionAmt:      pos.Quantity,
			EntryPrice:       pos.EntryPrice,
			MarkPrice:        pos.MarkPrice,
			UnrealizedProfit: pos.UnrealizedPnL,
			Leverage:         float64(pos.Leverage),
			LiquidationPrice: pos.LiquidationPrice,
		})
	}

	// 检测被动平仓（止损/止盈/强平/手动）
	closedPositions := at.detectClosedPositions(ctx.Positions)
	if len(closedPositions) > 0 {
		autoCloseActions := at.generateAutoCloseActions(closedPositions)

		// ✅ 为每个自动平仓矫正真实成交价格
		currentTime := time.Now().UnixMilli()
		for i := range autoCloseActions {
			action := &autoCloseActions[i]
			decision := &decision.Decision{
				Symbol: action.Symbol,
				Action: action.Action,
			}

			// 调用平仓价格矫正函数
			if err := at.verifyAndUpdateCloseFillPrice(decision, action, currentTime); err != nil {
				log.Printf("  ⚠️ 自动平仓成交价验证失败: %v", err)
			}
		}

		record.Decisions = append(record.Decisions, autoCloseActions...)
		log.Printf("🔔 检测到 %d 个被动平仓", len(closedPositions))
		for i, closed := range closedPositions {
			action := autoCloseActions[i]
			pnl := closed.Quantity * (closed.MarkPrice - closed.EntryPrice)
			if closed.Side == "short" {
				pnl = -pnl
			}
			pnlPct := pnl / (closed.EntryPrice * closed.Quantity) * 100 * float64(closed.Leverage)

			// 平仓原因中文映射
			reasonMap := map[string]string{
				"stop_loss":   "止损",
				"take_profit": "止盈",
				"liquidation": "强平",
				"unknown":     "未知",
			}
			reasonCN := reasonMap[action.Error]
			if reasonCN == "" {
				reasonCN = action.Error
			}

			log.Printf("   └─ %s %s | 开仓: %.4f → 平仓: %.4f | 盈亏: %+.2f%% | 原因: %s",
				closed.Symbol,
				closed.Side,
				closed.EntryPrice,
				action.Price, // 使用真实成交价格（已矫正）
				pnlPct,
				reasonCN)
		}

		// ✅ 清理被动平仓后残留的止损止盈订单 (Fix #76)
		// 当止损触发时，止盈订单会残留；当止盈触发时，止损订单会残留
		// 需要主动取消这些残留订单，避免订单累积
		for _, closed := range closedPositions {
			if err := at.trader.CancelAllOrders(closed.Symbol); err != nil {
				log.Printf("  ⚠️ 清理 %s 残留订单失败: %v", closed.Symbol, err)
			} else {
				log.Printf("  🧹 已清理 %s 的残留止损止盈订单", closed.Symbol)
			}
		}
	}

	log.Print(strings.Repeat("=", 70))
	for _, coin := range ctx.CandidateCoins {
		record.CandidateCoins = append(record.CandidateCoins, coin.Symbol)
	}

	log.Printf("📊 账户净值: %.2f USDT | 可用: %.2f USDT | 持仓: %d",
		ctx.Account.TotalEquity, ctx.Account.AvailableBalance, ctx.Account.PositionCount)

	// 5. 调用AI获取完整决策
	log.Printf("🤖 正在请求AI分析并决策... [模板: %s]", at.systemPromptTemplate)
	decision, err := decision.GetFullDecisionWithCustomPrompt(ctx, at.mcpClient, at.customPrompt, at.overrideBasePrompt, at.systemPromptTemplate)

	if decision != nil && decision.AIRequestDurationMs > 0 {
		record.AIRequestDurationMs = decision.AIRequestDurationMs
		log.Printf("⏱️ AI调用耗时: %.2f 秒", float64(record.AIRequestDurationMs)/1000)
		record.ExecutionLog = append(record.ExecutionLog,
			fmt.Sprintf("AI调用耗时: %d ms", record.AIRequestDurationMs))
	}

	// 即使有错误，也保存思维链、决策和输入prompt（用于debug）
	if decision != nil {
		record.SystemPrompt = decision.SystemPrompt // 保存系统提示词
		record.InputPrompt = decision.UserPrompt
		record.CoTTrace = decision.CoTTrace
		record.PromptHash = decision.PromptHash // 保存Prompt模板版本哈希
		if len(decision.Decisions) > 0 {
			decisionJSON, _ := json.MarshalIndent(decision.Decisions, "", "  ")
			record.DecisionJSON = string(decisionJSON)
		}
	}

	if err != nil {
		record.Success = false
		record.ErrorMessage = fmt.Sprintf("获取AI决策失败: %v", err)

		// 打印系统提示词和AI思维链（即使有错误，也要输出以便调试）
		if decision != nil {
			log.Print("\n" + strings.Repeat("=", 70) + "\n")
			log.Printf("📋 系统提示词 [模板: %s] (错误情况)", at.systemPromptTemplate)
			log.Println(strings.Repeat("=", 70))
			log.Println(decision.SystemPrompt)
			log.Println(strings.Repeat("=", 70))

			if decision.CoTTrace != "" {
				log.Print("\n" + strings.Repeat("-", 70) + "\n")
				log.Println("💭 AI思维链分析（错误情况）:")
				log.Println(strings.Repeat("-", 70))
				log.Println(decision.CoTTrace)
				log.Println(strings.Repeat("-", 70))
			}
		}

		at.decisionLogger.LogDecision(record)
		return fmt.Errorf("获取AI决策失败: %w", err)
	}

	// // 5. 打印系统提示词
	// log.Printf("\n" + strings.Repeat("=", 70))
	// log.Printf("📋 系统提示词 [模板: %s]", at.systemPromptTemplate)
	// log.Println(strings.Repeat("=", 70))
	// log.Println(decision.SystemPrompt)
	// log.Printf(strings.Repeat("=", 70) + "\n")

	// 6. 打印AI思维链
	// log.Printf("\n" + strings.Repeat("-", 70))
	// log.Println("💭 AI思维链分析:")
	// log.Println(strings.Repeat("-", 70))
	// log.Println(decision.CoTTrace)
	// log.Printf(strings.Repeat("-", 70) + "\n")

	// 7. 打印AI决策
	// log.Printf("📋 AI决策列表 (%d 个):\n", len(decision.Decisions))
	// for i, d := range decision.Decisions {
	//     log.Printf("  [%d] %s: %s - %s", i+1, d.Symbol, d.Action, d.Reasoning)
	//     if d.Action == "open_long" || d.Action == "open_short" {
	//        log.Printf("      杠杆: %dx | 仓位: %.2f USDT | 止损: %.4f | 止盈: %.4f",
	//           d.Leverage, d.PositionSizeUSD, d.StopLoss, d.TakeProfit)
	//     }
	// }
	log.Println()
	log.Print(strings.Repeat("-", 70))
	// 8. 对决策排序：确保先平仓后开仓（防止仓位叠加超限）
	log.Print(strings.Repeat("-", 70))

	// 8. 对决策排序：确保先平仓后开仓（防止仓位叠加超限）
	sortedDecisions := sortDecisionsByPriority(decision.Decisions)

	log.Println("🔄 执行顺序（已优化）: 先平仓→后开仓")
	for i, d := range sortedDecisions {
		log.Printf("  [%d] %s %s", i+1, d.Symbol, d.Action)
	}
	log.Println()

	// 执行决策并记录结果
	for _, d := range sortedDecisions {
		actionRecord := logger.DecisionAction{
			Action:    d.Action,
			Symbol:    d.Symbol,
			Quantity:  0,
			Leverage:  d.Leverage,
			Price:     0,
			Timestamp: time.Now(),
			Success:   false,
			Source:    d.Source,
			PlanID:    "",
		}

		if err := at.executeDecisionWithRecord(&d, &actionRecord); err != nil {
			log.Printf("❌ 执行决策失败 (%s %s): %v", d.Symbol, d.Action, err)
			actionRecord.Error = err.Error()
			record.ExecutionLog = append(record.ExecutionLog, fmt.Sprintf("❌ %s %s 失败: %v", d.Symbol, d.Action, err))
		} else {
			actionRecord.Success = true
			record.ExecutionLog = append(record.ExecutionLog, fmt.Sprintf("✓ %s %s 成功", d.Symbol, d.Action))
			// 成功执行后短暂延迟
			time.Sleep(1 * time.Second)
		}

		record.Decisions = append(record.Decisions, actionRecord)
	}

	// 9. 更新持仓快照（用于下一周期检测被动平仓）
	at.updatePositionSnapshot(ctx.Positions)

	// 10. 保存决策记录
	if err := at.decisionLogger.LogDecision(record); err != nil {
		log.Printf("⚠ 保存决策记录失败: %v", err)
	}

	return nil
}

// runMartingaleTick 执行马丁格尔策略的自动Tick
func (at *AutoTrader) runMartingaleTick(record *logger.DecisionRecord) error {
	at.cleanupMartingalePlansWithoutPositions()

	// 1. 检查挂单状态
	pendingSteps := at.martingaleManager.GetPendingSteps()
	for _, step := range pendingSteps {
		order, err := at.trader.GetOrder(step.Symbol, step.OrderID)
		if err != nil {
			log.Printf("⚠️ 检查订单状态失败 %s %d: %v", step.Symbol, step.OrderID, err)
			continue
		}

		status, ok := order["status"].(string)
		if !ok {
			continue
		}

		if status == "FILLED" {
			// 解析成交价格和数量
			fillPrice := 0.0
			if pStr, ok := order["avgPrice"].(string); ok {
				fillPrice, _ = strconv.ParseFloat(pStr, 64)
			}
			qty := 0.0
			if qStr, ok := order["executedQty"].(string); ok {
				qty, _ = strconv.ParseFloat(qStr, 64)
			}

			log.Printf("⚡ 马丁格尔限价单已成交: %s Step %d (价格: %.4f, 数量: %.4f)", step.Symbol, step.StepIndex, fillPrice, qty)

			actions, err := at.martingaleManager.NotifyFill(step.PlanID, step.StepIndex, fillPrice, qty)
			if err != nil {
				log.Printf("❌ 通知成交失败: %v", err)
				continue
			}

			// 执行后续动作（如预挂下一步限价单、更新止损止盈）
			for _, action := range actions {
				rec, err := at.executeMartingaleAction(action, action.PlanID)
				if err != nil {
					log.Printf("❌ 执行后续动作失败: %v", err)
					at.martingaleManager.NotifyActionFailure(action.PlanID, action.StepIndex)
				}
				if rec != nil {
					record.Decisions = append(record.Decisions, *rec)
					record.ExecutionLog = append(record.ExecutionLog, fmt.Sprintf("⚡ 马丁格尔自动执行: %s %s (Step %d)", action.Symbol, action.Type, action.StepIndex))
				}
			}
		}
	}

	symbols := at.martingaleManager.GetActiveSymbols()
	if len(symbols) == 0 {
		return nil
	}

	priceMap := make(map[string]float64)
	for _, sym := range symbols {
		data, err := market.Get(sym)
		if err != nil {
			log.Printf("获取价格失败 %s: %v", sym, err)
			continue
		}
		priceMap[sym] = data.CurrentPrice
	}

	actions := at.martingaleManager.TickAll(priceMap)
	if len(actions) > 0 {
		log.Printf("⚡ 触发马丁格尔自动操作: %d 个", len(actions))
	}

	for _, action := range actions {
		rec, err := at.executeMartingaleAction(action, action.PlanID)
		if err != nil {
			log.Printf("❌ 执行马丁格尔动作失败 (Plan %s Step %d): %v", action.PlanID, action.StepIndex, err)
			at.martingaleManager.NotifyActionFailure(action.PlanID, action.StepIndex)
		}
		if rec != nil {
			// 将自动执行的动作记录到决策日志中
			record.Decisions = append(record.Decisions, *rec)
			record.ExecutionLog = append(record.ExecutionLog, fmt.Sprintf("⚡ 马丁格尔自动执行: %s %s (Step %d)", action.Symbol, action.Type, action.StepIndex))
		}
	}
	return nil
}

func (at *AutoTrader) cleanupMartingalePlansWithoutPositions() {
	if at.martingaleManager == nil {
		return
	}

	plans := at.martingaleManager.GetPlans()
	if len(plans) == 0 {
		return
	}

	positions, err := at.trader.GetPositions()
	if err != nil {
		log.Printf("⚠️ 获取持仓失败，无法清理马丁格尔计划: %v", err)
		return
	}

	activePositions := make(map[string]bool)
	for _, pos := range positions {
		symbol, _ := pos["symbol"].(string)
		side, _ := pos["side"].(string)
		qty, _ := pos["positionAmt"].(float64)
		if symbol == "" || side == "" {
			continue
		}
		if math.Abs(qty) < 1e-9 {
			continue
		}
		key := strings.ToUpper(symbol) + "_" + strings.ToLower(side)
		activePositions[key] = true
	}

	for _, plan := range plans {
		if plan == nil {
			continue
		}
		// 跳过已完成的计划（可能只是历史记录）
		if plan.Completed {
			continue
		}
		key := strings.ToUpper(plan.Symbol) + "_" + strings.ToLower(plan.Side)
		if activePositions[key] {
			continue
		}
		// 取消关联的限价挂单（防止孤立挂单后续成交）
		if err := at.trader.CancelAllOrders(plan.Symbol); err != nil {
			log.Printf("⚠️ 取消 %s 挂单失败: %v", plan.Symbol, err)
		}
		if err := at.martingaleManager.DeletePlan(plan.ID); err != nil {
			log.Printf("⚠️ 删除无持仓的马丁格尔计划失败 (Plan %s): %v", plan.ID, err)
			continue
		}
		log.Printf("🧹 %s (%s) 无持仓，自动销毁马丁格尔计划", plan.Symbol, plan.ID)
	}
}

// buildTradingContext 构建交易上下文
func (at *AutoTrader) buildTradingContext() (*decision.Context, error) {
	// 1. 获取账户信息
	balance, err := at.trader.GetBalance()
	if err != nil {
		return nil, fmt.Errorf("获取账户余额失败: %w", err)
	}

	// 获取账户字段
	totalWalletBalance := 0.0
	totalUnrealizedProfit := 0.0
	availableBalance := 0.0

	if wallet, ok := balance["totalWalletBalance"].(float64); ok {
		totalWalletBalance = wallet
	}
	if unrealized, ok := balance["totalUnrealizedProfit"].(float64); ok {
		totalUnrealizedProfit = unrealized
	}
	if avail, ok := balance["availableBalance"].(float64); ok {
		availableBalance = avail
	}

	// Total Equity = 钱包余额 + 未实现盈亏
	totalEquity := totalWalletBalance + totalUnrealizedProfit

	// 2. 获取持仓信息
	positions, err := at.trader.GetPositions()
	if err != nil {
		return nil, fmt.Errorf("获取持仓失败: %w", err)
	}

	var positionInfos []decision.PositionInfo
	totalMarginUsed := 0.0

	// 当前持仓的key集合（用于清理已平仓的记录）
	currentPositionKeys := make(map[string]bool)

	for _, pos := range positions {
		symbol := pos["symbol"].(string)
		side := pos["side"].(string)

		quantity := pos["positionAmt"].(float64)
		if quantity < 0 {
			quantity = -quantity
		}
		entryPrice := pos["entryPrice"].(float64)
		markPrice := pos["markPrice"].(float64)

		// 跳过已平仓的持仓（quantity = 0），防止"幽灵持仓"传递给AI
		if quantity == 0 {
			continue
		}

		unrealizedPnl := pos["unRealizedProfit"].(float64)
		liquidationPrice := pos["liquidationPrice"].(float64)

		// 计算占用保证金（基于开仓价）
		leverage := 10 // 默认值，实际应该从持仓信息获取
		if lev, ok := pos["leverage"].(float64); ok {
			leverage = int(lev)
		}
		marginUsed := (quantity * entryPrice) / float64(leverage)
		totalMarginUsed += marginUsed

		// 计算盈亏百分比（基于保证金，考虑杠杆）
		pnlPct := calculatePnLPercentage(unrealizedPnl, marginUsed)

		// 跟踪持仓首次出现时间
		posKey := symbol + "_" + side
		currentPositionKeys[posKey] = true
		if _, exists := at.positionFirstSeenTime[posKey]; !exists {
			// 尝试从 decision_logs 恢复开仓时间和止损止盈 (Issue #102)
			if openPos := at.decisionLogger.GetOpenPosition(symbol); openPos != nil && openPos.Side == side {
				at.positionFirstSeenTime[posKey] = openPos.OpenTime.UnixMilli()
				// 同时恢复止损止盈价格
				if openPos.StopLoss > 0 {
					at.positionStopLoss[posKey] = openPos.StopLoss
				}
				if openPos.TakeProfit > 0 {
					at.positionTakeProfit[posKey] = openPos.TakeProfit
				}
				log.Printf("✓ 从历史记录恢复持仓: %s %s, 开仓时间: %s, 止损: %.4f, 止盈: %.4f",
					symbol, side, openPos.OpenTime.Format("2006-01-02 15:04:05"),
					openPos.StopLoss, openPos.TakeProfit)
			} else {
				// 没有历史记录，设置为 0 表示未知
				at.positionFirstSeenTime[posKey] = 0
				log.Printf("⚠️ 未找到持仓历史记录: %s %s, 持仓时间未知", symbol, side)
			}
		}
		updateTime := at.positionFirstSeenTime[posKey]

		// 获取该持仓的历史最高收益率
		at.peakPnLCacheMutex.RLock()
		peakPnlPct := at.peakPnLCache[posKey]
		at.peakPnLCacheMutex.RUnlock()

		// 获取止损止盈价格（用于后续推断平仓原因）
		stopLoss := at.positionStopLoss[posKey]
		takeProfit := at.positionTakeProfit[posKey]

		// 确定持仓来源
		source := "manual"
		if at.martingaleManager != nil && at.martingaleManager.IsManagedPosition(symbol, side) {
			source = "martingale"
		}

		positionInfos = append(positionInfos, decision.PositionInfo{
			Symbol:           symbol,
			Side:             side,
			EntryPrice:       entryPrice,
			MarkPrice:        markPrice,
			Quantity:         quantity,
			Leverage:         leverage,
			UnrealizedPnL:    unrealizedPnl,
			UnrealizedPnLPct: pnlPct,
			PeakPnLPct:       peakPnlPct,
			LiquidationPrice: liquidationPrice,
			MarginUsed:       marginUsed,
			UpdateTime:       updateTime,
			StopLoss:         stopLoss,
			TakeProfit:       takeProfit,
			Source:           source,
		})
	}

	// 清理已平仓的持仓记录（包括止损止盈记录）
	for key := range at.positionFirstSeenTime {
		if !currentPositionKeys[key] {
			delete(at.positionFirstSeenTime, key)
			delete(at.positionStopLoss, key)
			delete(at.positionTakeProfit, key)
		}
	}

	// 3. 获取交易员的候选币种池
	candidateCoins, err := at.getCandidateCoins()
	if err != nil {
		return nil, fmt.Errorf("获取候选币种失败: %w", err)
	}

	// 4. 计算总盈亏
	totalPnL := totalEquity - at.initialBalance
	totalPnLPct := 0.0
	if at.initialBalance > 0 {
		totalPnLPct = (totalPnL / at.initialBalance) * 100
	}

	marginUsedPct := 0.0
	if totalEquity > 0 {
		marginUsedPct = (totalMarginUsed / totalEquity) * 100
	}

	// 5. 分析历史表现（使用缓存机制，避免每次扫描文件）
	// 🚀 使用统一的缓存懒加载逻辑（首次扫描1000周期，后续使用缓存）
	// AI 只需要最近 20 笔交易作为参考
	// filterByPrompt=false: AI 训练时使用所有历史交易数据（不按 PromptHash 过滤）
	performance, err := at.decisionLogger.GetPerformanceWithCache(20, false)
	if err != nil {
		log.Printf("⚠️  分析历史表现失败: %v", err)
		// 不影响主流程，继续执行（但设置performance为nil以避免传递错误数据）
		performance = nil
	}

	// 6. 获取运行中的马丁格尔计划
	var martingalePlans []decision.MartingalePlanInfo
	if at.martingaleManager != nil {
		for _, plan := range at.martingaleManager.GetPlans() {
			if plan == nil || plan.Completed {
				continue
			}
			filledSteps := 0
			var stepsInfo []decision.MartingaleStepInfo
			for _, step := range plan.Steps {
				if step.Filled {
					filledSteps++
				}
				stepsInfo = append(stepsInfo, decision.MartingaleStepInfo{
					Index:        step.Index,
					TriggerPrice: step.TriggerPrice,
					Filled:       step.Filled,
					Pending:      step.Pending,
				})
			}
			martingalePlans = append(martingalePlans, decision.MartingalePlanInfo{
				ID:              plan.ID[:8], // 只取前8位
				Symbol:          plan.Symbol,
				Side:            plan.Side,
				TotalQty:        plan.TotalQty,
				AvgEntry:        plan.AvgEntry,
				CurrentStopLoss: plan.CurrentStopLoss,
				CurrentTP:       plan.CurrentTakeProfit,
				FilledSteps:     filledSteps,
				MaxSteps:        len(plan.Steps),
				Closing:         plan.Closing,
				Steps:           stepsInfo,
			})
		}
	}

	// 7. 构建上下文
	ctx := &decision.Context{
		CurrentTime:     time.Now().Format("2006-01-02 15:04:05"),
		RuntimeMinutes:  int(time.Since(at.startTime).Minutes()),
		CallCount:       at.callCount,
		Exchange:        at.exchange,               // 交易所名称
		BTCETHLeverage:  at.config.BTCETHLeverage,  // 使用配置的杠杆倍数
		AltcoinLeverage: at.config.AltcoinLeverage, // 使用配置的杠杆倍数
		Account: decision.AccountInfo{
			TotalEquity:      totalEquity,
			AvailableBalance: availableBalance,
			UnrealizedPnL:    totalUnrealizedProfit,
			TotalPnL:         totalPnL,
			TotalPnLPct:      totalPnLPct,
			MarginUsed:       totalMarginUsed,
			MarginUsedPct:    marginUsedPct,
			PositionCount:    len(positionInfos),
		},
		Positions:       positionInfos,
		CandidateCoins:  candidateCoins,
		MartingalePlans: martingalePlans,
		Performance:     performance, // 添加历史表现分析
	}

	return ctx, nil
}

// executeDecisionWithRecord 执行AI决策并记录详细信息
func (at *AutoTrader) executeDecisionWithRecord(decision *decision.Decision, actionRecord *logger.DecisionAction) error {
	switch decision.Action {
	case "open_long":
		return at.executeOpenLongWithRecord(decision, actionRecord)
	case "open_short":
		return at.executeOpenShortWithRecord(decision, actionRecord)
	case "close_long":
		return at.executeCloseLongWithRecord(decision, actionRecord)
	case "close_short":
		return at.executeCloseShortWithRecord(decision, actionRecord)
	case "update_stop_loss":
		return at.executeUpdateStopLossWithRecord(decision, actionRecord)
	case "update_take_profit":
		return at.executeUpdateTakeProfitWithRecord(decision, actionRecord)
	case "partial_close":
		return at.executePartialCloseWithRecord(decision, actionRecord)
	case "use_tool":
		return at.executeUseToolWithRecord(decision, actionRecord)
	case "hold", "wait":
		// 无需执行，仅记录
		return nil
	default:
		return fmt.Errorf("未知的action: %s", decision.Action)
	}
}

// executeUseToolWithRecord 执行工具调用并记录详细信息
func (at *AutoTrader) executeUseToolWithRecord(decision *decision.Decision, actionRecord *logger.DecisionAction) error {
	log.Printf("  🛠️ 调用工具: %s (%s)", decision.Tool, decision.Symbol)

	if decision.Tool == "martingale" {
		return at.executeMartingaleTool(decision, actionRecord)
	}

	return fmt.Errorf("未知的工具: %s", decision.Tool)
}

// executeMartingaleTool 执行马丁格尔工具逻辑
func (at *AutoTrader) executeMartingaleTool(d *decision.Decision, actionRecord *logger.DecisionAction) error {
	// 1. 解析配置
	var cfg martingale.Config
	if err := json.Unmarshal(d.ToolConfig, &cfg); err != nil {
		return fmt.Errorf("解析马丁格尔配置失败: %w", err)
	}
	// 确保 Symbol 和 Side 与决策一致
	if cfg.Symbol == "" {
		cfg.Symbol = d.Symbol
	}

	// 处理更新操作
	if cfg.Action == "update" {
		if cfg.Side == "" {
			return fmt.Errorf("side is required for update")
		}
		// 查找活跃计划
		plan := at.martingaleManager.GetActivePlan(cfg.Symbol, cfg.Side)
		if plan == nil {
			return fmt.Errorf("未找到 %s %s 的活跃马丁格尔计划", cfg.Symbol, cfg.Side)
		}

		log.Printf("  📝 更新马丁格尔计划 (ID: %s) - %d 个更新项", plan.ID, len(cfg.Updates))

		var totalActions []martingale.Action
		for _, update := range cfg.Updates {
			// 调用 Manager 更新步骤价格
			actions, err := at.martingaleManager.UpdateStepPrice(plan.ID, update.StepIndex, update.NewPrice)
			if err != nil {
				log.Printf("  ⚠️ 更新步骤 %d 失败: %v", update.StepIndex, err)
				continue
			}
			if len(actions) > 0 {
				totalActions = append(totalActions, actions...)
			}
		}

		// 执行产生的动作（通常是 CancelOrder）
		for _, action := range totalActions {
			if _, err := at.executeMartingaleAction(action, plan.ID); err != nil {
				log.Printf("  ❌ 执行更新动作失败: %v", err)
			}
		}

		actionRecord.Success = true
		actionRecord.ExecutionDetails = fmt.Sprintf("马丁格尔计划已更新 ID: %s", plan.ID)
		return nil
	}

	// 处理创建操作 (默认)
	// 2. 获取当前价格
	marketData, err := market.Get(cfg.Symbol)
	if err != nil {
		return fmt.Errorf("获取市场价格失败: %w", err)
	}
	currentPrice := marketData.CurrentPrice

	// 3. 启动马丁格尔计划
	// 检查是否已有计划
	if existingPlan := at.martingaleManager.GetActivePlan(cfg.Symbol, cfg.Side); existingPlan != nil {
		return fmt.Errorf("已存在 %s %s 的活跃马丁格尔计划 (ID: %s)，请先停止或使用 update", cfg.Symbol, cfg.Side, existingPlan.ID)
	}

	defaultLeverage := at.config.AltcoinLeverage
	if strings.Contains(cfg.Symbol, "BTC") || strings.Contains(cfg.Symbol, "ETH") {
		defaultLeverage = at.config.BTCETHLeverage
	}

	plan, actions, err := at.martingaleManager.CreatePlan(cfg, currentPrice, defaultLeverage)
	if err != nil {
		return fmt.Errorf("启动马丁格尔计划失败: %w", err)
	}

	log.Printf("  ✓ 马丁格尔计划已创建 (ID: %s), 准备执行 %d 个初始动作", plan.ID, len(actions))

	// 4. 执行生成的动作
	if len(actions) == 0 {
		return fmt.Errorf("马丁格尔计划未生成任何动作")
	}

	// 关键修复：先注册计划，确保 NotifyFill 能找到它
	// 如果首单失败，再删除计划
	if err := at.martingaleManager.RegisterPlan(plan); err != nil {
		return fmt.Errorf("注册马丁格尔计划失败: %w", err)
	}
	log.Printf("  ✅ 马丁格尔计划已注册 (ID: %s)", plan.ID)

	firstAction := actions[0]
	if _, err := at.executeMartingaleAction(firstAction, plan.ID); err != nil {
		log.Printf("  ❌ 执行首个马丁格尔动作失败: %v", err)
		// 首单失败，删除已注册的计划
		if delErr := at.martingaleManager.DeletePlan(plan.ID); delErr != nil {
			log.Printf("  ⚠️ 回滚删除计划失败: %v", delErr)
		}
		return err
	}
	log.Printf("  ✅ 首单执行成功，计划已持久化 (ID: %s)", plan.ID)

	// 执行剩余动作
	for i := 1; i < len(actions); i++ {
		action := actions[i]
		if _, err := at.executeMartingaleAction(action, plan.ID); err != nil {
			log.Printf("  ❌ 执行后续马丁格尔动作失败: %v", err)
			at.martingaleManager.NotifyActionFailure(plan.ID, action.StepIndex)
		}
	}

	actionRecord.Success = true
	actionRecord.ExecutionDetails = fmt.Sprintf("马丁格尔计划启动 ID: %s", plan.ID)
	return nil
}

// executeMartingaleAction 执行马丁格尔管理器生成的动作
func (at *AutoTrader) executeMartingaleAction(action martingale.Action, planID string) (*logger.DecisionAction, error) {
	log.Printf("  → 执行马丁格尔动作: %s %s %s (Step %d)", action.Symbol, action.Side, action.Type, action.StepIndex)

	switch action.Type {
	case martingale.ActionOpen:
		// 构造开仓决策
		d := &decision.Decision{
			Symbol:          action.Symbol,
			Action:          "open_" + action.Side, // open_long or open_short
			Leverage:        action.Leverage,
			PositionSizeUSD: action.PositionSizeUSD,
			StopLoss:        action.StopLoss,
			TakeProfit:      action.TakeProfit,
			Reasoning:       action.Reason,
			Source:          "martingale", // 标识来源
		}
		rec := &logger.DecisionAction{
			Action:    d.Action,
			Symbol:    d.Symbol,
			Timestamp: time.Now(),
			Source:    d.Source,
			PlanID:    planID,
		}

		// 复用现有的开仓逻辑
		var err error
		if action.Side == "long" {
			err = at.executeOpenLongWithRecord(d, rec)
		} else {
			err = at.executeOpenShortWithRecord(d, rec)
		}

		if err != nil {
			rec.Success = false
			rec.Error = err.Error()
			return rec, err
		}
		rec.Success = true

		// 通知管理器成交（简化处理：假设完全成交，价格为当前市价）
		// 实际生产中应获取真实成交价和数量
		// 这里我们再次获取价格作为近似成交价
		marketData, _ := market.Get(action.Symbol)
		fillPrice := marketData.CurrentPrice
		if rec.Price > 0 {
			fillPrice = rec.Price
		}
		qty := rec.Quantity // 从记录中获取计算出的数量

		// 异步通知管理器（避免死锁，虽然 Manager 锁是独立的）
		// 但为了保持状态一致性，这里同步调用
		nextActions, err := at.martingaleManager.NotifyFill(planID, action.StepIndex, fillPrice, qty)
		if err != nil {
			log.Printf("  ⚠️ 通知马丁格尔成交失败: %v", err)
		} else if len(nextActions) > 0 {
			// 递归执行后续动作（如更新止损止盈）
			for _, nextAction := range nextActions {
				if _, err := at.executeMartingaleAction(nextAction, planID); err != nil {
					log.Printf("  ❌ 执行后续动作失败: %v", err)
				}
			}
		}
		return rec, nil

	case martingale.ActionPlaceLimit:
		// 构造限价单决策
		rec := &logger.DecisionAction{
			Action:    "limit_" + action.Side,
			Symbol:    action.Symbol,
			Timestamp: time.Now(),
			Price:     action.Price,
			Quantity:  action.PositionSizeUSD / action.Price,
			Source:    "martingale",
			PlanID:    planID,
		}

		quantity := action.PositionSizeUSD / action.Price
		var order map[string]interface{}
		var err error

		if action.Side == "long" {
			order, err = at.trader.OpenLimitLong(action.Symbol, quantity, action.Price, action.Leverage)
		} else {
			order, err = at.trader.OpenLimitShort(action.Symbol, quantity, action.Price, action.Leverage)
		}

		if err != nil {
			rec.Success = false
			rec.Error = err.Error()
			return rec, err
		}

		rec.Success = true
		if orderID, ok := order["orderId"].(int64); ok {
			rec.OrderID = orderID
			at.martingaleManager.NotifyOrderPlaced(planID, action.StepIndex, orderID)
		}
		return rec, nil

	case martingale.ActionUpdateStop:
		d := &decision.Decision{
			Symbol:      action.Symbol,
			Action:      "update_stop_loss",
			NewStopLoss: action.NewStopLoss,
			Reasoning:   action.Reason,
		}
		rec := &logger.DecisionAction{Action: d.Action, Symbol: d.Symbol, Timestamp: time.Now(), Source: d.Source, PlanID: planID}
		err := at.executeUpdateStopLossWithRecord(d, rec)
		if err != nil {
			rec.Success = false
			rec.Error = err.Error()
		} else {
			rec.Success = true
		}
		return rec, err

	case martingale.ActionUpdateTake:
		d := &decision.Decision{
			Symbol:        action.Symbol,
			Action:        "update_take_profit",
			NewTakeProfit: action.NewTakeProfit,
			Reasoning:     action.Reason,
		}
		rec := &logger.DecisionAction{Action: d.Action, Symbol: d.Symbol, Timestamp: time.Now(), Source: d.Source, PlanID: planID}
		err := at.executeUpdateTakeProfitWithRecord(d, rec)
		if err != nil {
			rec.Success = false
			rec.Error = err.Error()
		} else {
			rec.Success = true
		}
		return rec, err

	case martingale.ActionCancelOrder:
		log.Printf("  🚫 取消旧订单 (ID: %d)", action.OrderID)
		// 尝试取消订单，即使失败（例如已成交/已取消）也继续，确保流程不阻塞
		// 注意：CancelOrder 接口通常只需要 Symbol 和 OrderId
		if err := at.trader.CancelOrder(action.Symbol, action.OrderID); err != nil {
			log.Printf("  ⚠️ 取消订单失败 (可能已不存在): %v", err)
		}
		return &logger.DecisionAction{
			Action:    "cancel_order",
			Symbol:    action.Symbol,
			OrderID:   action.OrderID,
			Timestamp: time.Now(),
			Success:   true,
			Source:    "martingale",
			PlanID:    planID,
		}, nil

	case martingale.ActionCloseAll:
		d := &decision.Decision{
			Symbol:    action.Symbol,
			Action:    "close_" + action.Side,
			Reasoning: action.Reason,
			Source:    "martingale", // 标识来源
		}
		rec := &logger.DecisionAction{Action: d.Action, Symbol: d.Symbol, Timestamp: time.Now(), Source: d.Source, PlanID: planID}
		var err error
		if action.Side == "long" {
			err = at.executeCloseLongWithRecord(d, rec)
		} else {
			err = at.executeCloseShortWithRecord(d, rec)
		}
		if err == nil {
			rec.Success = true
			at.martingaleManager.NotifyClosed(planID)
		} else {
			rec.Success = false
			rec.Error = err.Error()
		}
		return rec, err
	}

	return nil, nil
}

func computeMarginRequirements(positionSizeUSD float64, leverage int) (requiredMargin, estimatedFee, totalRequired float64) {
	effectiveLeverage := float64(leverage)
	if effectiveLeverage <= 0 {
		effectiveLeverage = 1
	}
	absPositionSize := math.Abs(positionSizeUSD)
	if absPositionSize == 0 {
		return 0, 0, 0
	}
	requiredMargin = absPositionSize / effectiveLeverage
	estimatedFee = absPositionSize * takerFeeRate
	totalRequired = requiredMargin + estimatedFee
	return
}

func adjustPositionSizeToBalance(positionSizeUSD, availableBalance float64, leverage int) (float64, float64, float64, bool) {
	if availableBalance <= 0 {
		return 0, 0, 0, false
	}
	usableBalance := availableBalance * maxMarginUsageRatio
	if usableBalance <= 0 {
		return 0, 0, 0, false
	}
	effectiveLeverage := float64(leverage)
	if effectiveLeverage <= 0 {
		effectiveLeverage = 1
	}
	denominator := (1.0 / effectiveLeverage) + takerFeeRate
	if denominator <= 0 {
		denominator = 1.0 / effectiveLeverage
	}
	adjustedPositionSize := usableBalance / denominator
	if adjustedPositionSize <= 0 {
		return 0, 0, 0, false
	}
	originalSize := math.Abs(positionSizeUSD)
	if originalSize > 0 && adjustedPositionSize > originalSize {
		adjustedPositionSize = originalSize
	}
	requiredMargin, estimatedFee, totalRequired := computeMarginRequirements(adjustedPositionSize, leverage)
	if totalRequired > availableBalance {
		return 0, 0, 0, false
	}
	return adjustedPositionSize, requiredMargin, estimatedFee, true
}

// executeOpenLongWithRecord 执行开多仓并记录详细信息
func (at *AutoTrader) executeOpenLongWithRecord(decision *decision.Decision, actionRecord *logger.DecisionAction) error {
	log.Printf("  📈 开多仓: %s", decision.Symbol)

	// ⚠️ 关键：检查是否已有同币种同方向持仓，如果有则拒绝开仓（防止仓位叠加超限）
	// 例外：Martingale 策略允许加仓 (DCA)
	positions, err := at.trader.GetPositions()
	if err == nil {
		for _, pos := range positions {
			if pos["symbol"] == decision.Symbol && pos["side"] == "long" {
				// 如果是 Martingale 策略的加仓操作，允许通过
				if decision.Source == "martingale" {
					log.Printf("  🛡️ Martingale 加仓 (DCA): %s", decision.Symbol)
					continue
				}
				return fmt.Errorf("❌ %s 已有多仓，拒绝开仓以防止仓位叠加超限。如需换仓，请先给出 close_long 决策", decision.Symbol)
			}
		}
	}

	// 获取当前价格
	marketData, err := market.Get(decision.Symbol)
	if err != nil {
		return err
	}

	positionSizeUSD := decision.PositionSizeUSD
	actionRecord.Price = marketData.CurrentPrice

	// ⚠️ 保证金验证：防止保证金不足错误（code=-2019）
	requiredMargin, estimatedFee, totalRequired := computeMarginRequirements(positionSizeUSD, decision.Leverage)

	balance, err := at.trader.GetBalance()
	if err != nil {
		return fmt.Errorf("获取账户余额失败: %w", err)
	}
	availableBalance := 0.0
	if avail, ok := balance["availableBalance"].(float64); ok {
		availableBalance = avail
	}

	if totalRequired > availableBalance {
		adjustedSizeUSD, adjustedMargin, adjustedFee, ok := adjustPositionSizeToBalance(positionSizeUSD, availableBalance, decision.Leverage)
		if !ok {
			return fmt.Errorf("❌ 保证金不足: 需要 %.2f USDT（保证金 %.2f + 手续费 %.2f），可用 %.2f USDT",
				totalRequired, requiredMargin, estimatedFee, availableBalance)
		}
		log.Printf("  ⚠️ 可用余额不足，自动缩减仓位: %.2f → %.2f USDT", positionSizeUSD, adjustedSizeUSD)
		positionSizeUSD = adjustedSizeUSD
		requiredMargin = adjustedMargin
		estimatedFee = adjustedFee
		totalRequired = requiredMargin + estimatedFee
	}

	if totalRequired > availableBalance {
		return fmt.Errorf("❌ 保证金不足: 需要 %.2f USDT（保证金 %.2f + 手续费 %.2f），可用 %.2f USDT",
			totalRequired, requiredMargin, estimatedFee, availableBalance)
	}

	quantity := positionSizeUSD / marketData.CurrentPrice
	actionRecord.Quantity = quantity

	// 设置仓位模式
	if err := at.trader.SetMarginMode(decision.Symbol, at.config.IsCrossMargin); err != nil {
		log.Printf("  ⚠️ 设置仓位模式失败: %v", err)
		// 继续执行，不影响交易
	}

	// 记录开仓时间（在开仓前记录）
	openTime := time.Now().UnixMilli()

	// 开仓
	order, err := at.trader.OpenLong(decision.Symbol, quantity, decision.Leverage)
	if err != nil {
		return err
	}

	// 记录订单ID
	if orderID, ok := order["orderId"].(int64); ok {
		actionRecord.OrderID = orderID
	}

	log.Printf("  ✓ 开仓成功，订单ID: %v, 数量: %.4f", order["orderId"], quantity)

	// 记录开仓时间到持仓跟踪
	posKey := decision.Symbol + "_long"
	at.positionFirstSeenTime[posKey] = openTime

	// 设置止损止盈
	if err := at.trader.SetStopLoss(decision.Symbol, "LONG", quantity, decision.StopLoss); err != nil {
		log.Printf("  ⚠ 设置止损失败: %v", err)
	} else {
		at.positionStopLoss[posKey] = decision.StopLoss // 记录止损价格
		actionRecord.StopLoss = decision.StopLoss       // Issue #102: 记录到日志用于重启恢复
	}
	if err := at.trader.SetTakeProfit(decision.Symbol, "LONG", quantity, decision.TakeProfit); err != nil {
		log.Printf("  ⚠ 设置止盈失败: %v", err)
	} else {
		at.positionTakeProfit[posKey] = decision.TakeProfit // 记录止盈价格
		actionRecord.TakeProfit = decision.TakeProfit       // Issue #102: 记录到日志用于重启恢复
	}

	// ✅ 验证实际成交价格和风险（基于实际成交数据）
	if err := at.verifyAndUpdateActualFillPrice(decision, actionRecord, "long", marketData.CurrentPrice, openTime); err != nil {
		log.Printf("  ⚠️ 实际成交价验证失败: %v", err)
		// 不阻断流程，继续执行
	}

	return nil
}

// executeOpenShortWithRecord 执行开空仓并记录详细信息
func (at *AutoTrader) executeOpenShortWithRecord(decision *decision.Decision, actionRecord *logger.DecisionAction) error {
	log.Printf("  📉 开空仓: %s", decision.Symbol)

	// ⚠️ 关键：检查是否已有同币种同方向持仓，如果有则拒绝开仓（防止仓位叠加超限）
	// 例外：Martingale 策略允许加仓 (DCA)
	positions, err := at.trader.GetPositions()
	if err == nil {
		for _, pos := range positions {
			if pos["symbol"] == decision.Symbol && pos["side"] == "short" {
				// 如果是 Martingale 策略的加仓操作，允许通过
				if decision.Source == "martingale" {
					log.Printf("  🛡️ Martingale 加仓 (DCA): %s", decision.Symbol)
					continue
				}
				return fmt.Errorf("❌ %s 已有空仓，拒绝开仓以防止仓位叠加超限。如需换仓，请先给出 close_short 决策", decision.Symbol)
			}
		}
	}

	// 获取当前价格
	marketData, err := market.Get(decision.Symbol)
	if err != nil {
		return err
	}

	positionSizeUSD := decision.PositionSizeUSD
	actionRecord.Price = marketData.CurrentPrice

	// ⚠️ 保证金验证：防止保证金不足错误（code=-2019）
	requiredMargin, estimatedFee, totalRequired := computeMarginRequirements(positionSizeUSD, decision.Leverage)

	balance, err := at.trader.GetBalance()
	if err != nil {
		return fmt.Errorf("获取账户余额失败: %w", err)
	}
	availableBalance := 0.0
	if avail, ok := balance["availableBalance"].(float64); ok {
		availableBalance = avail
	}

	if totalRequired > availableBalance {
		adjustedSizeUSD, adjustedMargin, adjustedFee, ok := adjustPositionSizeToBalance(positionSizeUSD, availableBalance, decision.Leverage)
		if !ok {
			return fmt.Errorf("❌ 保证金不足: 需要 %.2f USDT（保证金 %.2f + 手续费 %.2f），可用 %.2f USDT",
				totalRequired, requiredMargin, estimatedFee, availableBalance)
		}
		log.Printf("  ⚠️ 可用余额不足，自动缩减仓位: %.2f → %.2f USDT", positionSizeUSD, adjustedSizeUSD)
		positionSizeUSD = adjustedSizeUSD
		requiredMargin = adjustedMargin
		estimatedFee = adjustedFee
		totalRequired = requiredMargin + estimatedFee
	}

	if totalRequired > availableBalance {
		return fmt.Errorf("❌ 保证金不足: 需要 %.2f USDT（保证金 %.2f + 手续费 %.2f），可用 %.2f USDT",
			totalRequired, requiredMargin, estimatedFee, availableBalance)
	}

	quantity := positionSizeUSD / marketData.CurrentPrice
	actionRecord.Quantity = quantity

	// 设置仓位模式
	if err := at.trader.SetMarginMode(decision.Symbol, at.config.IsCrossMargin); err != nil {
		log.Printf("  ⚠️ 设置仓位模式失败: %v", err)
		// 继续执行，不影响交易
	}

	// 记录开仓时间（在开仓前记录）
	openTime := time.Now().UnixMilli()

	// 开仓
	order, err := at.trader.OpenShort(decision.Symbol, quantity, decision.Leverage)
	if err != nil {
		return err
	}

	// 记录订单ID
	if orderID, ok := order["orderId"].(int64); ok {
		actionRecord.OrderID = orderID
	}

	log.Printf("  ✓ 开仓成功，订单ID: %v, 数量: %.4f", order["orderId"], quantity)

	// 记录开仓时间到持仓跟踪
	posKey := decision.Symbol + "_short"
	at.positionFirstSeenTime[posKey] = openTime

	// 设置止损止盈
	if err := at.trader.SetStopLoss(decision.Symbol, "SHORT", quantity, decision.StopLoss); err != nil {
		log.Printf("  ⚠ 设置止损失败: %v", err)
	} else {
		at.positionStopLoss[posKey] = decision.StopLoss // 记录止损价格
		actionRecord.StopLoss = decision.StopLoss       // Issue #102: 记录到日志用于重启恢复
	}
	if err := at.trader.SetTakeProfit(decision.Symbol, "SHORT", quantity, decision.TakeProfit); err != nil {
		log.Printf("  ⚠ 设置止盈失败: %v", err)
	} else {
		at.positionTakeProfit[posKey] = decision.TakeProfit // 记录止盈价格
		actionRecord.TakeProfit = decision.TakeProfit       // Issue #102: 记录到日志用于重启恢复
	}

	// ✅ 验证实际成交价格和风险（基于实际成交数据）
	if err := at.verifyAndUpdateActualFillPrice(decision, actionRecord, "short", marketData.CurrentPrice, openTime); err != nil {
		log.Printf("  ⚠️ 实际成交价验证失败: %v", err)
		// 不阻断流程，继续执行
	}

	return nil
}

// executeCloseLongWithRecord 执行平多仓并记录详细信息
func (at *AutoTrader) executeCloseLongWithRecord(decision *decision.Decision, actionRecord *logger.DecisionAction) error {
	log.Printf("  🔄 平多仓: %s", decision.Symbol)

	// 🛡️ 已移除 Martingale 保护限制：允许任何来源平仓马丁格尔策略的持仓

	// 获取当前价格
	marketData, err := market.Get(decision.Symbol)
	if err != nil {
		return err
	}
	actionRecord.Price = marketData.CurrentPrice

	// 记录平仓时间
	closeTime := time.Now().UnixMilli()

	// 平仓
	order, err := at.trader.CloseLong(decision.Symbol, 0) // 0 = 全部平仓
	if err != nil {
		return err
	}

	// 记录订单ID
	if orderID, ok := order["orderId"].(int64); ok {
		actionRecord.OrderID = orderID
	}

	log.Printf("  ✓ 平仓成功")

	// ✅ 验证实际成交价格（基于交易所成交记录）
	if err := at.verifyAndUpdateCloseFillPrice(decision, actionRecord, closeTime); err != nil {
		log.Printf("  ⚠️ 平仓成交价验证失败: %v", err)
		// 不阻断流程，继续执行
	}

	return nil
}

// executeCloseShortWithRecord 执行平空仓并记录详细信息
func (at *AutoTrader) executeCloseShortWithRecord(decision *decision.Decision, actionRecord *logger.DecisionAction) error {
	log.Printf("  🔄 平空仓: %s", decision.Symbol)

	// 🛡️ 已移除 Martingale 保护限制：允许任何来源平仓马丁格尔策略的持仓

	// 获取当前价格
	marketData, err := market.Get(decision.Symbol)
	if err != nil {
		return err
	}
	actionRecord.Price = marketData.CurrentPrice

	// 记录平仓时间
	closeTime := time.Now().UnixMilli()

	// 平仓
	order, err := at.trader.CloseShort(decision.Symbol, 0) // 0 = 全部平仓
	if err != nil {
		return err
	}

	// 记录订单ID
	if orderID, ok := order["orderId"].(int64); ok {
		actionRecord.OrderID = orderID
	}

	log.Printf("  ✓ 平仓成功")

	// ✅ 验证实际成交价格（基于交易所成交记录）
	if err := at.verifyAndUpdateCloseFillPrice(decision, actionRecord, closeTime); err != nil {
		log.Printf("  ⚠️ 平仓成交价验证失败: %v", err)
		// 不阻断流程，继续执行
	}

	return nil
}

// executeUpdateStopLossWithRecord 执行调整止损并记录详细信息
func (at *AutoTrader) executeUpdateStopLossWithRecord(decision *decision.Decision, actionRecord *logger.DecisionAction) error {
	log.Printf("  🎯 调整止损: %s → %.2f", decision.Symbol, decision.NewStopLoss)

	// 获取当前价格
	marketData, err := market.Get(decision.Symbol)
	if err != nil {
		return err
	}
	actionRecord.Price = marketData.CurrentPrice
	actionRecord.NewStopLoss = decision.NewStopLoss // 记录新止损价格（用于前端显示）

	// 获取当前持仓
	positions, err := at.trader.GetPositions()
	if err != nil {
		return fmt.Errorf("获取持仓失败: %w", err)
	}

	// 查找目标持仓
	var targetPosition map[string]interface{}
	for _, pos := range positions {
		symbol, _ := pos["symbol"].(string)
		posAmt, _ := pos["positionAmt"].(float64)
		if symbol == decision.Symbol && posAmt != 0 {
			targetPosition = pos
			break
		}
	}

	if targetPosition == nil {
		return fmt.Errorf("持仓不存在: %s", decision.Symbol)
	}

	// 获取持仓方向和数量
	side, _ := targetPosition["side"].(string)
	positionSide := strings.ToUpper(side)
	positionAmt, _ := targetPosition["positionAmt"].(float64)

	// 验证新止损价格合理性
	if positionSide == "LONG" && decision.NewStopLoss >= marketData.CurrentPrice {
		return fmt.Errorf("多单止损必须低于当前价格 (当前: %.2f, 新止损: %.2f)", marketData.CurrentPrice, decision.NewStopLoss)
	}
	if positionSide == "SHORT" && decision.NewStopLoss <= marketData.CurrentPrice {
		return fmt.Errorf("空单止损必须高于当前价格 (当前: %.2f, 新止损: %.2f)", marketData.CurrentPrice, decision.NewStopLoss)
	}

	// ⚠️ 防御性检查：检测是否存在双向持仓（不应该出现，但提供保护）
	var hasOppositePosition bool
	oppositeSide := ""
	for _, pos := range positions {
		symbol, _ := pos["symbol"].(string)
		posSide, _ := pos["side"].(string)
		posAmt, _ := pos["positionAmt"].(float64)
		if symbol == decision.Symbol && posAmt != 0 && strings.ToUpper(posSide) != positionSide {
			hasOppositePosition = true
			oppositeSide = strings.ToUpper(posSide)
			break
		}
	}

	if hasOppositePosition {
		log.Printf("  🚨 警告：检测到 %s 存在双向持仓（%s + %s），这违反了策略规则",
			decision.Symbol, positionSide, oppositeSide)
		log.Printf("  🚨 取消止损单将影响两个方向的订单，请检查是否为用户手动操作导致")
		log.Printf("  🚨 建议：手动平掉其中一个方向的持仓，或检查系统是否有BUG")
	}

	// 检查是否与当前止损相同，避免重复操作
	posKey := decision.Symbol + "_" + strings.ToLower(positionSide)
	currentStopLoss := at.positionStopLoss[posKey]

	// ⚠️ 核心保护：防止止损倒退（Ratchet Stop Loss）
	// 只有在 currentStopLoss > 0 (已设置过止损) 时才检查
	if currentStopLoss > 0 {
		// 多单：新止损必须 >= 当前止损（只能上移）
		if positionSide == "LONG" && decision.NewStopLoss < currentStopLoss {
			log.Printf("  🚫 拒绝回调止损 (Long): 新止损 %.2f < 当前止损 %.2f (禁止向下移动)",
				decision.NewStopLoss, currentStopLoss)
			return nil // 视为成功但不执行，避免AI报错重试
		}
		// 空单：新止损必须 <= 当前止损（只能下移）
		if positionSide == "SHORT" && decision.NewStopLoss > currentStopLoss {
			log.Printf("  🚫 拒绝回调止损 (Short): 新止损 %.2f > 当前止损 %.2f (禁止向上移动)",
				decision.NewStopLoss, currentStopLoss)
			return nil // 视为成功但不执行
		}
	}

	if math.Abs(currentStopLoss-decision.NewStopLoss) < 0.01 {
		log.Printf("  ℹ️  新止损价格(%.2f)与当前止损(%.2f)相同，跳过操作", decision.NewStopLoss, currentStopLoss)
		return nil
	}

	// ⚠️ Save-Restore Pattern: 保存当前止盈价格，因为某些交易所（如 Hyperliquid）
	// 取消止损时会连带取消所有挂单（包括止盈），需要在设置新止损后恢复止盈
	savedTakeProfit := at.positionTakeProfit[posKey]

	// 取消旧的止损单
	// 注意：Hyperliquid 会删除所有挂单，但我们会在后面恢复止盈
	if err := at.trader.CancelStopLossOrders(decision.Symbol); err != nil {
		log.Printf("  ⚠ 取消旧止损单失败: %v", err)
		// 不中断执行，继续设置新止损
	}

	// 调用交易所 API 修改止损
	quantity := math.Abs(positionAmt)
	err = at.trader.SetStopLoss(decision.Symbol, positionSide, quantity, decision.NewStopLoss)
	if err != nil {
		return fmt.Errorf("修改止损失败: %w", err)
	}

	log.Printf("  ✓ 止损已调整: %.2f (当前价格: %.2f)", decision.NewStopLoss, marketData.CurrentPrice)

	// 更新内存中的止损价格
	at.positionStopLoss[posKey] = decision.NewStopLoss

	// ⚠️ Save-Restore: 仅 Hyperliquid 需要恢复被误删的止盈单
	// 因为 Hyperliquid 的 CancelStopLossOrders 会删除所有挂单（包括止盈）
	// 其他交易所（如 Binance）的 CancelStopLossOrders 只删除止损，不需要恢复
	if at.exchange == "hyperliquid" && savedTakeProfit > 0 {
		log.Printf("  🔄 [Hyperliquid Restore] 检测到止盈单被误删，正在恢复止盈: %.2f", savedTakeProfit)
		if err := at.trader.SetTakeProfit(decision.Symbol, positionSide, quantity, savedTakeProfit); err != nil {
			log.Printf("  ⚠️ [Restore] 恢复止盈单失败: %v（止盈可能丢失，请检查）", err)
			// 不中断流程，但记录警告
		} else {
			log.Printf("  ✓ [Restore] 止盈单已恢复: %.2f", savedTakeProfit)
		}
	}

	return nil
}

// executeUpdateTakeProfitWithRecord 执行调整止盈并记录详细信息
func (at *AutoTrader) executeUpdateTakeProfitWithRecord(decision *decision.Decision, actionRecord *logger.DecisionAction) error {
	log.Printf("  🎯 调整止盈: %s → %.2f", decision.Symbol, decision.NewTakeProfit)

	// 获取当前价格
	marketData, err := market.Get(decision.Symbol)
	if err != nil {
		return err
	}
	actionRecord.Price = marketData.CurrentPrice
	actionRecord.NewTakeProfit = decision.NewTakeProfit // 记录新止盈价格（用于前端显示）

	// 获取当前持仓
	positions, err := at.trader.GetPositions()
	if err != nil {
		return fmt.Errorf("获取持仓失败: %w", err)
	}

	// 查找目标持仓
	var targetPosition map[string]interface{}
	for _, pos := range positions {
		symbol, _ := pos["symbol"].(string)
		posAmt, _ := pos["positionAmt"].(float64)
		if symbol == decision.Symbol && posAmt != 0 {
			targetPosition = pos
			break
		}
	}

	if targetPosition == nil {
		return fmt.Errorf("持仓不存在: %s", decision.Symbol)
	}

	// 获取持仓方向和数量
	side, _ := targetPosition["side"].(string)
	positionSide := strings.ToUpper(side)
	positionAmt, _ := targetPosition["positionAmt"].(float64)

	// 验证新止盈价格合理性
	if positionSide == "LONG" && decision.NewTakeProfit <= marketData.CurrentPrice {
		return fmt.Errorf("多单止盈必须高于当前价格 (当前: %.2f, 新止盈: %.2f)", marketData.CurrentPrice, decision.NewTakeProfit)
	}
	if positionSide == "SHORT" && decision.NewTakeProfit >= marketData.CurrentPrice {
		return fmt.Errorf("空单止盈必须低于当前价格 (当前: %.2f, 新止盈: %.2f)", marketData.CurrentPrice, decision.NewTakeProfit)
	}

	// ⚠️ 防御性检查：检测是否存在双向持仓（不应该出现，但提供保护）
	var hasOppositePosition bool
	oppositeSide := ""
	for _, pos := range positions {
		symbol, _ := pos["symbol"].(string)
		posSide, _ := pos["side"].(string)
		posAmt, _ := pos["positionAmt"].(float64)
		if symbol == decision.Symbol && posAmt != 0 && strings.ToUpper(posSide) != positionSide {
			hasOppositePosition = true
			oppositeSide = strings.ToUpper(posSide)
			break
		}
	}

	if hasOppositePosition {
		log.Printf("  🚨 警告：检测到 %s 存在双向持仓（%s + %s），这违反了策略规则",
			decision.Symbol, positionSide, oppositeSide)
		log.Printf("  🚨  取消止盈单将影响两个方向的订单，请检查是否为用户手动操作导致")
		log.Printf("  🚨 建议：手动平掉其中一个方向的持仓，或检查系统是否有BUG")
	}

	// 检查是否与当前止盈相同，避免重复操作
	posKey := decision.Symbol + "_" + strings.ToLower(positionSide)
	currentTakeProfit := at.positionTakeProfit[posKey]
	if math.Abs(currentTakeProfit-decision.NewTakeProfit) < 0.01 {
		log.Printf("  ℹ️  新止盈价格(%.2f)与当前止盈(%.2f)相同，跳过操作", decision.NewTakeProfit, currentTakeProfit)
		return nil
	}

	// ⚠️ Save-Restore Pattern: 保存当前止损价格，因为某些交易所（如 Hyperliquid）
	// 取消止盈时会连带取消所有挂单（包括止损），需要在设置新止盈后恢复止损
	savedStopLoss := at.positionStopLoss[posKey]

	// 取消旧的止盈单
	// 注意：Hyperliquid 会删除所有挂单，但我们会在后面恢复止损
	if err := at.trader.CancelTakeProfitOrders(decision.Symbol); err != nil {
		log.Printf("  ⚠ 取消旧止盈单失败: %v", err)
		// 不中断执行，继续设置新止盈
	}

	// 调用交易所 API 修改止盈
	quantity := math.Abs(positionAmt)
	err = at.trader.SetTakeProfit(decision.Symbol, positionSide, quantity, decision.NewTakeProfit)
	if err != nil {
		return fmt.Errorf("修改止盈失败: %w", err)
	}

	log.Printf("  ✓ 止盈已调整: %.2f (当前价格: %.2f)", decision.NewTakeProfit, marketData.CurrentPrice)

	// 更新内存中的止盈价格
	at.positionTakeProfit[posKey] = decision.NewTakeProfit

	// ⚠️ Save-Restore: 仅 Hyperliquid 需要恢复被误删的止损单
	// 因为 Hyperliquid 的 CancelTakeProfitOrders 会删除所有挂单（包括止损）
	// 其他交易所（如 Binance）的 CancelTakeProfitOrders 只删除止盈，不需要恢复
	if at.exchange == "hyperliquid" && savedStopLoss > 0 {
		log.Printf("  🔄 [Hyperliquid Restore] 检测到止损单被误删，正在恢复止损: %.2f", savedStopLoss)
		if err := at.trader.SetStopLoss(decision.Symbol, positionSide, quantity, savedStopLoss); err != nil {
			log.Printf("  ⚠️ [Restore] 恢复止损单失败: %v（止损可能丢失，请检查）", err)
			// 不中断流程，但记录警告
		} else {
			log.Printf("  ✓ [Restore] 止损单已恢复: %.2f", savedStopLoss)
		}
	}

	return nil
}

// executePartialCloseWithRecord 执行部分平仓并记录详细信息
func (at *AutoTrader) executePartialCloseWithRecord(decision *decision.Decision, actionRecord *logger.DecisionAction) error {
	log.Printf("  📊 部分平仓: %s %.1f%%", decision.Symbol, decision.ClosePercentage)

	// 🛡️ Martingale 保护：禁止AI/手动部分平仓马丁格尔策略的持仓
	// (Partial Close 不太可能是 Martingale 的行为，除非将来 Martingale 支持部分止盈)
	// 已移除 Martingale 保护限制：允许任何来源进行部分平仓

	// 验证百分比范围
	if decision.ClosePercentage <= 0 || decision.ClosePercentage > 100 {
		return fmt.Errorf("平仓百分比必须在 0-100 之间，当前: %.1f", decision.ClosePercentage)
	}

	// 获取当前价格
	marketData, err := market.Get(decision.Symbol)
	if err != nil {
		return err
	}
	actionRecord.Price = marketData.CurrentPrice
	actionRecord.ClosePercentage = decision.ClosePercentage // 记录平仓百分比（用于前端显示）

	// 获取当前持仓
	positions, err := at.trader.GetPositions()
	if err != nil {
		return fmt.Errorf("获取持仓失败: %w", err)
	}

	// 查找目标持仓

	var targetPosition map[string]interface{}
	for _, pos := range positions {
		symbol, _ := pos["symbol"].(string)
		posAmt, _ := pos["positionAmt"].(float64)
		if symbol == decision.Symbol && posAmt != 0 {
			targetPosition = pos
			break
		}
	}

	if targetPosition == nil {
		return fmt.Errorf("持仓不存在: %s", decision.Symbol)
	}

	// 获取持仓方向和数量
	side, _ := targetPosition["side"].(string)
	positionSide := strings.ToUpper(side)
	positionAmt, _ := targetPosition["positionAmt"].(float64)

	// 计算平仓数量
	totalQuantity := math.Abs(positionAmt)
	closeQuantity := totalQuantity * (decision.ClosePercentage / 100.0)
	actionRecord.Quantity = closeQuantity

	// ✅ Layer 2: 最小仓位检查（防止产生小额剩余）
	markPrice, ok := targetPosition["markPrice"].(float64)
	if !ok || markPrice <= 0 {
		return fmt.Errorf("无法解析当前价格，无法执行最小仓位检查")
	}

	currentPositionValue := totalQuantity * markPrice
	remainingQuantity := totalQuantity - closeQuantity
	remainingValue := remainingQuantity * markPrice

	const MIN_POSITION_VALUE = 10.0 // 最小持仓价值 10 USDT（對齊交易所底线，小仓位建议直接全平）

	if remainingValue > 0 && remainingValue <= MIN_POSITION_VALUE {
		log.Printf("⚠️ 检测到 partial_close 后剩余仓位 %.2f USDT < %.0f USDT",
			remainingValue, MIN_POSITION_VALUE)
		log.Printf("  → 当前仓位价值: %.2f USDT, 平仓 %.1f%%, 剩余: %.2f USDT",
			currentPositionValue, decision.ClosePercentage, remainingValue)
		log.Printf("  → 自动修正为全部平仓，避免产生无法平仓的小额剩余")

		// 🔄 自动修正为全部平仓
		if positionSide == "LONG" {
			decision.Action = "close_long"
			log.Printf("  ✓ 已修正为: close_long")
			return at.executeCloseLongWithRecord(decision, actionRecord)
		} else {
			decision.Action = "close_short"
			log.Printf("  ✓ 已修正为: close_short")
			return at.executeCloseShortWithRecord(decision, actionRecord)
		}
	}

	// 记录平仓时间（用于后续验证真实成交价格）
	closeTime := time.Now().UnixMilli()

	// 执行平仓
	var order map[string]interface{}
	if positionSide == "LONG" {
		order, err = at.trader.CloseLong(decision.Symbol, closeQuantity)
	} else {
		order, err = at.trader.CloseShort(decision.Symbol, closeQuantity)
	}

	if err != nil {
		return fmt.Errorf("部分平仓失败: %w", err)
	}

	// 记录订单ID
	if orderID, ok := order["orderId"].(int64); ok {
		actionRecord.OrderID = orderID
	}

	log.Printf("  ✓ 部分平仓成功: 平仓 %.4f (%.1f%%), 剩余 %.4f",
		closeQuantity, decision.ClosePercentage, remainingQuantity)

	// ✅ 验证实际成交价格（基于交易所成交记录）
	if err := at.verifyAndUpdateCloseFillPrice(decision, actionRecord, closeTime); err != nil {
		log.Printf("  ⚠️ 部分平仓成交价验证失败: %v", err)
	}

	// ✅ Step 4: 重新创建止盈止损订单（使用剩余数量）
	// 部分平仓时，CloseLong/CloseShort 会取消所有挂单（因为原订单数量不正确）
	// 需要用剩余数量重新创建 SL/TP 订单
	// 优先使用 AI 提供的新价格，否则使用内存中缓存的原始价格
	posKey := decision.Symbol + "_" + strings.ToLower(positionSide)
	originalStopLoss := at.positionStopLoss[posKey]
	originalTakeProfit := at.positionTakeProfit[posKey]

	// 确定最终使用的 SL/TP 价格
	finalStopLoss := decision.NewStopLoss
	if finalStopLoss <= 0 {
		finalStopLoss = originalStopLoss
	}
	finalTakeProfit := decision.NewTakeProfit
	if finalTakeProfit <= 0 {
		finalTakeProfit = originalTakeProfit
	}

	// 重新创建止损订单
	if finalStopLoss > 0 {
		if decision.NewStopLoss > 0 {
			log.Printf("  → 设置剩余仓位 %.4f 的新止损价: %.4f (AI指定)", remainingQuantity, finalStopLoss)
		} else {
			log.Printf("  → 恢复剩余仓位 %.4f 的原止损价: %.4f", remainingQuantity, finalStopLoss)
		}
		err = at.trader.SetStopLoss(decision.Symbol, positionSide, remainingQuantity, finalStopLoss)
		if err != nil {
			log.Printf("  ⚠️ 设置止损失败: %v（不影响平仓结果）", err)
		} else {
			// ✅ 更新内存缓存，确保后续操作使用最新的 SL 价格
			at.positionStopLoss[posKey] = finalStopLoss
		}
	}

	// 重新创建止盈订单
	if finalTakeProfit > 0 {
		if decision.NewTakeProfit > 0 {
			log.Printf("  → 设置剩余仓位 %.4f 的新止盈价: %.4f (AI指定)", remainingQuantity, finalTakeProfit)
		} else {
			log.Printf("  → 恢复剩余仓位 %.4f 的原止盈价: %.4f", remainingQuantity, finalTakeProfit)
		}
		err = at.trader.SetTakeProfit(decision.Symbol, positionSide, remainingQuantity, finalTakeProfit)
		if err != nil {
			log.Printf("  ⚠️ 设置止盈失败: %v（不影响平仓结果）", err)
		} else {
			// ✅ 更新内存缓存，确保后续操作使用最新的 TP 价格
			at.positionTakeProfit[posKey] = finalTakeProfit
		}
	}

	return nil
}

// GetID 获取trader ID
func (at *AutoTrader) GetID() string {
	return at.id
}

// GetName 获取trader名称
func (at *AutoTrader) GetName() string {
	return at.name
}

// GetAIModel 获取AI模型
func (at *AutoTrader) GetAIModel() string {
	return at.aiModel
}

// GetExchange 获取交易所
func (at *AutoTrader) GetExchange() string {
	return at.exchange
}

// GetConfig 获取交易配置（用于测试）
func (at *AutoTrader) GetConfig() AutoTraderConfig {
	return at.config
}

// SetCustomPrompt 设置自定义交易策略prompt
func (at *AutoTrader) SetCustomPrompt(prompt string) {
	at.customPrompt = prompt
}

// SetOverrideBasePrompt 设置是否覆盖基础prompt
func (at *AutoTrader) SetOverrideBasePrompt(override bool) {
	at.overrideBasePrompt = override
}

// SetSystemPromptTemplate 设置系统提示词模板
func (at *AutoTrader) SetSystemPromptTemplate(templateName string) {
	at.systemPromptTemplate = templateName
}

// GetSystemPromptTemplate 获取当前系统提示词模板名称
func (at *AutoTrader) GetSystemPromptTemplate() string {
	return at.systemPromptTemplate
}

// GetTradingCoins 获取交易币种列表
func (at *AutoTrader) GetTradingCoins() []string {
	return at.tradingCoins
}

// GetDecisionLogger 获取决策日志记录器
func (at *AutoTrader) GetDecisionLogger() logger.IDecisionLogger {
	return at.decisionLogger
}

// GetStatus 获取系统状态（用于API）
func (at *AutoTrader) GetStatus() map[string]interface{} {
	aiProvider := "DeepSeek"
	if at.config.UseQwen {
		aiProvider = "Qwen"
	}

	// 使用读锁保护并发访问的字段
	at.statusMutex.RLock()
	isRunning := at.isRunning
	startTime := at.startTime
	callCount := at.callCount
	at.statusMutex.RUnlock()

	return map[string]interface{}{
		"trader_id":       at.id,
		"trader_name":     at.name,
		"ai_model":        at.aiModel,
		"exchange":        at.exchange,
		"is_running":      isRunning,
		"start_time":      startTime.Format(time.RFC3339),
		"runtime_minutes": int(time.Since(startTime).Minutes()),
		"call_count":      callCount,
		"initial_balance": at.initialBalance,
		"scan_interval":   at.config.ScanInterval.String(),
		"stop_until":      at.stopUntil.Format(time.RFC3339),
		"last_reset_time": at.lastResetTime.Format(time.RFC3339),
		"ai_provider":     aiProvider,
	}
}

// GetAccountInfo 获取账户信息（用于API）
func (at *AutoTrader) GetAccountInfo() (map[string]interface{}, error) {
	balance, err := at.trader.GetBalance()
	if err != nil {
		return nil, fmt.Errorf("获取余额失败: %w", err)
	}

	// 获取账户字段
	totalWalletBalance := 0.0
	totalUnrealizedProfit := 0.0
	availableBalance := 0.0

	if wallet, ok := balance["totalWalletBalance"].(float64); ok {
		totalWalletBalance = wallet
	}
	if unrealized, ok := balance["totalUnrealizedProfit"].(float64); ok {
		totalUnrealizedProfit = unrealized
	}
	if avail, ok := balance["availableBalance"].(float64); ok {
		availableBalance = avail
	}

	// Total Equity = 钱包余额 + 未实现盈亏
	totalEquity := totalWalletBalance + totalUnrealizedProfit

	// 获取持仓计算总保证金
	positions, err := at.trader.GetPositions()
	if err != nil {
		return nil, fmt.Errorf("获取持仓失败: %w", err)
	}

	totalMarginUsed := 0.0
	totalUnrealizedPnLCalculated := 0.0
	for _, pos := range positions {
		entryPrice := pos["entryPrice"].(float64)
		quantity := pos["positionAmt"].(float64)
		if quantity < 0 {
			quantity = -quantity
		}
		unrealizedPnl := pos["unRealizedProfit"].(float64)
		totalUnrealizedPnLCalculated += unrealizedPnl

		leverage := 10
		if lev, ok := pos["leverage"].(float64); ok {
			leverage = int(lev)
		}
		marginUsed := (quantity * entryPrice) / float64(leverage)
		totalMarginUsed += marginUsed
	}

	// 验证未实现盈亏的一致性（API值 vs 从持仓计算）
	diff := math.Abs(totalUnrealizedProfit - totalUnrealizedPnLCalculated)
	if diff > 0.1 { // 允许0.01 USDT的误差
		log.Printf("⚠️ 未实现盈亏不一致: API=%.4f, 计算=%.4f, 差异=%.4f",
			totalUnrealizedProfit, totalUnrealizedPnLCalculated, diff)
	}

	totalPnL := totalEquity - at.initialBalance
	totalPnLPct := 0.0
	if at.initialBalance > 0 {
		totalPnLPct = (totalPnL / at.initialBalance) * 100
	} else {
		log.Printf("⚠️ Initial Balance异常: %.2f，无法计算PNL百分比", at.initialBalance)
	}

	marginUsedPct := 0.0
	if totalEquity > 0 {
		marginUsedPct = (totalMarginUsed / totalEquity) * 100
	}

	return map[string]interface{}{
		// 核心字段
		"total_equity":      totalEquity,           // 账户净值 = wallet + unrealized
		"wallet_balance":    totalWalletBalance,    // 钱包余额（不含未实现盈亏）
		"unrealized_profit": totalUnrealizedProfit, // 未实现盈亏（交易所API官方值）
		"available_balance": availableBalance,      // 可用余额

		// 盈亏统计
		"total_pnl":       totalPnL,          // 总盈亏 = equity - initial
		"total_pnl_pct":   totalPnLPct,       // 总盈亏百分比
		"initial_balance": at.initialBalance, // 初始余额
		"daily_pnl":       at.dailyPnL,       // 日盈亏

		// 持仓信息
		"position_count":  len(positions),  // 持仓数量
		"margin_used":     totalMarginUsed, // 保证金占用
		"margin_used_pct": marginUsedPct,   // 保证金使用率
	}, nil
}

// GetPositions 获取持仓列表（用于API）
func (at *AutoTrader) GetPositions() ([]map[string]interface{}, error) {
	positions, err := at.trader.GetPositions()
	if err != nil {
		return nil, fmt.Errorf("获取持仓失败: %w", err)
	}

	var result []map[string]interface{}
	for _, pos := range positions {
		symbol := pos["symbol"].(string)
		side := pos["side"].(string)

		// 马丁格尔托管的仓位由策略自行管理，避免重复回撤风控
		if at.martingaleManager != nil && at.martingaleManager.IsManagedPosition(symbol, side) {
			continue
		}
		entryPrice := pos["entryPrice"].(float64)
		markPrice := pos["markPrice"].(float64)
		quantity := pos["positionAmt"].(float64)
		if quantity < 0 {
			quantity = -quantity // 空仓数量为负，转为正数
		}
		unrealizedPnl := pos["unRealizedProfit"].(float64)
		liquidationPrice := pos["liquidationPrice"].(float64)

		leverage := 10
		if lev, ok := pos["leverage"].(float64); ok {
			leverage = int(lev)
		}

		// 计算占用保证金（基于开仓价，而非当前价）
		marginUsed := (quantity * entryPrice) / float64(leverage)

		// 计算盈亏百分比（基于保证金）
		pnlPct := calculatePnLPercentage(unrealizedPnl, marginUsed)

		// 确定持仓来源
		source := "manual"
		if at.martingaleManager != nil && at.martingaleManager.IsManagedPosition(symbol, side) {
			source = "martingale"
		}

		result = append(result, map[string]interface{}{
			"symbol":             symbol,
			"side":               side,
			"entry_price":        entryPrice,
			"mark_price":         markPrice,
			"quantity":           quantity,
			"leverage":           leverage,
			"unrealized_pnl":     unrealizedPnl,
			"unrealized_pnl_pct": pnlPct,
			"liquidation_price":  liquidationPrice,
			"margin_used":        marginUsed,
			"source":             source,
		})
	}

	return result, nil
}

// calculatePnLPercentage 计算盈亏百分比（基于保证金，自动考虑杠杆）
// 收益率 = 未实现盈亏 / 保证金 × 100%
func calculatePnLPercentage(unrealizedPnl, marginUsed float64) float64 {
	if marginUsed > 0 {
		return (unrealizedPnl / marginUsed) * 100
	}
	return 0.0
}

// sortDecisionsByPriority 对决策排序：先平仓，再开仓，最后hold/wait
// 这样可以避免换仓时仓位叠加超限
func sortDecisionsByPriority(decisions []decision.Decision) []decision.Decision {
	if len(decisions) <= 1 {
		return decisions
	}

	// 定义优先级
	getActionPriority := func(action string) int {
		switch action {
		case "close_long", "close_short", "partial_close":
			return 1 // 最高优先级：先平仓（包括部分平仓）
		case "update_stop_loss", "update_take_profit":
			return 2 // 调整持仓止盈止损
		case "open_long", "open_short":
			return 3 // 次优先级：后开仓
		case "hold", "wait":
			return 4 // 最低优先级：观望
		default:
			return 999 // 未知动作放最后
		}
	}

	// 复制决策列表
	sorted := make([]decision.Decision, len(decisions))
	copy(sorted, decisions)

	// 按优先级排序
	for i := 0; i < len(sorted)-1; i++ {
		for j := i + 1; j < len(sorted); j++ {
			if getActionPriority(sorted[i].Action) > getActionPriority(sorted[j].Action) {
				sorted[i], sorted[j] = sorted[j], sorted[i]
			}
		}
	}

	return sorted
}

// getCandidateCoins 获取交易员的候选币种列表
func (at *AutoTrader) getCandidateCoins() ([]decision.CandidateCoin, error) {
	if len(at.tradingCoins) == 0 {
		// 使用数据库配置的默认币种列表
		var candidateCoins []decision.CandidateCoin

		if len(at.defaultCoins) > 0 {
			// 使用数据库中配置的默认币种
			for _, coin := range at.defaultCoins {
				symbol := normalizeSymbol(coin)
				candidateCoins = append(candidateCoins, decision.CandidateCoin{
					Symbol:  symbol,
					Sources: []string{"default"}, // 标记为数据库默认币种
				})
			}
			log.Printf("📋 [%s] 使用数据库默认币种: %d个币种 %v",
				at.name, len(candidateCoins), at.defaultCoins)
			return candidateCoins, nil
		} else {
			// 如果数据库中没有配置默认币种，则使用AI500+OI Top作为fallback
			const ai500Limit = 20 // AI500取前20个评分最高的币种

			mergedPool, err := pool.GetMergedCoinPool(ai500Limit)
			if err != nil {
				return nil, fmt.Errorf("获取合并币种池失败: %w", err)
			}

			// 构建候选币种列表（包含来源信息）
			for _, symbol := range mergedPool.AllSymbols {
				sources := mergedPool.SymbolSources[symbol]
				candidateCoins = append(candidateCoins, decision.CandidateCoin{
					Symbol:  symbol,
					Sources: sources, // "ai500" 和/或 "oi_top"
				})
			}

			log.Printf("📋 [%s] 数据库无默认币种配置，使用AI500+OI Top: AI500前%d + OI_Top20 = 总计%d个候选币种",
				at.name, ai500Limit, len(candidateCoins))
			return candidateCoins, nil
		}
	} else {
		// 使用自定义币种列表
		var candidateCoins []decision.CandidateCoin
		for _, coin := range at.tradingCoins {
			// 确保币种格式正确（转为大写USDT交易对）
			symbol := normalizeSymbol(coin)
			candidateCoins = append(candidateCoins, decision.CandidateCoin{
				Symbol:  symbol,
				Sources: []string{"custom"}, // 标记为自定义来源
			})
		}

		log.Printf("📋 [%s] 使用自定义币种: %d个币种 %v",
			at.name, len(candidateCoins), at.tradingCoins)
		return candidateCoins, nil
	}
}

// normalizeSymbol 标准化币种符号（确保以USDT结尾）
func normalizeSymbol(symbol string) string {
	// 转为大写
	symbol = strings.ToUpper(strings.TrimSpace(symbol))

	// 确保以USDT结尾
	if !strings.HasSuffix(symbol, "USDT") {
		symbol = symbol + "USDT"
	}

	return symbol
}

// 启动回撤监控
func (at *AutoTrader) startDrawdownMonitor() {
	at.monitorWg.Add(1)
	go func() {
		defer at.monitorWg.Done()

		ticker := time.NewTicker(1 * time.Minute) // 每分钟检查一次
		defer ticker.Stop()

		log.Println("📊 启动持仓回撤监控（每分钟检查一次）")

		for {
			select {
			case <-ticker.C:
				at.checkPositionDrawdown()
			case <-at.stopMonitorCh:
				log.Println("⏹ 停止持仓回撤监控")
				return
			}
		}
	}()
}

// 检查持仓回撤情况
func (at *AutoTrader) checkPositionDrawdown() {
	// 获取当前持仓
	positions, err := at.trader.GetPositions()
	if err != nil {
		log.Printf("❌ 回撤监控：获取持仓失败: %v", err)
		return
	}

	for _, pos := range positions {
		symbol := pos["symbol"].(string)
		side := pos["side"].(string)

		// 马丁格尔托管的仓位由策略自行处理，跳过统一回撤风控
		if at.martingaleManager != nil && at.martingaleManager.IsManagedPosition(symbol, side) {
			continue
		}
		entryPrice := pos["entryPrice"].(float64)
		markPrice := pos["markPrice"].(float64)

		// 计算当前盈亏百分比
		leverage := 10 // 默认值
		if lev, ok := pos["leverage"].(float64); ok {
			leverage = int(lev)
		}

		var currentPnLPct float64
		if side == "long" {
			currentPnLPct = ((markPrice - entryPrice) / entryPrice) * float64(leverage) * 100
		} else {
			currentPnLPct = ((entryPrice - markPrice) / entryPrice) * float64(leverage) * 100
		}

		// 构造持仓唯一标识（区分多空）
		posKey := symbol + "_" + side

		// 获取该持仓的历史最高收益
		at.peakPnLCacheMutex.RLock()
		peakPnLPct, exists := at.peakPnLCache[posKey]
		at.peakPnLCacheMutex.RUnlock()

		if !exists {
			// 如果没有历史最高记录，使用当前盈亏作为初始值
			peakPnLPct = currentPnLPct
			at.UpdatePeakPnL(symbol, side, currentPnLPct)
		} else {
			// 更新峰值缓存
			at.UpdatePeakPnL(symbol, side, currentPnLPct)
		}

		// 计算回撤（从最高点下跌的幅度）
		var drawdownPct float64
		if peakPnLPct > 0 && currentPnLPct < peakPnLPct {
			drawdownPct = ((peakPnLPct - currentPnLPct) / peakPnLPct) * 100
		}

		// 检查平仓条件：收益大于5%且回撤超过40%
		// 🛡️ Martingale 保护：马丁格尔策略有自己的风控逻辑，不受此全局回撤保护限制
		if at.martingaleManager != nil && at.martingaleManager.IsManagedPosition(symbol, side) {
			// 仅记录日志，不执行操作
			if currentPnLPct > 5.0 && drawdownPct >= 40.0 {
				log.Printf("🛡️ Martingale持仓触发全局回撤条件 (忽略): %s %s | 回撤: %.2f%%", symbol, side, drawdownPct)
			}
			continue
		}

		if currentPnLPct > 5.0 && drawdownPct >= 40.0 {
			log.Printf("🚨 触发回撤平仓条件: %s %s | 当前收益: %.2f%% | 最高收益: %.2f%% | 回撤: %.2f%%",
				symbol, side, currentPnLPct, peakPnLPct, drawdownPct)

			// 执行平仓
			if err := at.emergencyClosePosition(symbol, side); err != nil {
				log.Printf("❌ 回撤平仓失败 (%s %s): %v", symbol, side, err)
			} else {
				log.Printf("✅ 回撤平仓成功: %s %s", symbol, side)
				// 平仓后清理该持仓的缓存
				at.ClearPeakPnLCache(symbol, side)
			}
		} else if currentPnLPct > 5.0 {
			// 记录接近平仓条件的情况（用于调试）
			log.Printf("📊 回撤监控: %s %s | 收益: %.2f%% | 最高: %.2f%% | 回撤: %.2f%%",
				symbol, side, currentPnLPct, peakPnLPct, drawdownPct)
		}
	}
}

// 紧急平仓函数
func (at *AutoTrader) emergencyClosePosition(symbol, side string) error {
	switch side {
	case "long":
		order, err := at.trader.CloseLong(symbol, 0) // 0 = 全部平仓
		if err != nil {
			return err
		}
		log.Printf("✅ 紧急平多仓成功，订单ID: %v", order["orderId"])
	case "short":
		order, err := at.trader.CloseShort(symbol, 0) // 0 = 全部平仓
		if err != nil {
			return err
		}
		log.Printf("✅ 紧急平空仓成功，订单ID: %v", order["orderId"])
	default:
		return fmt.Errorf("未知的持仓方向: %s", side)
	}

	return nil
}

// GetPeakPnLCache 获取最高收益缓存
func (at *AutoTrader) GetPeakPnLCache() map[string]float64 {
	at.peakPnLCacheMutex.RLock()
	defer at.peakPnLCacheMutex.RUnlock()

	// 返回缓存的副本
	cache := make(map[string]float64)
	for k, v := range at.peakPnLCache {
		cache[k] = v
	}
	return cache
}

// UpdatePeakPnL 更新最高收益缓存
func (at *AutoTrader) UpdatePeakPnL(symbol, side string, currentPnLPct float64) {
	at.peakPnLCacheMutex.Lock()
	defer at.peakPnLCacheMutex.Unlock()

	posKey := symbol + "_" + side
	if peak, exists := at.peakPnLCache[posKey]; exists {
		// 更新峰值（如果是多头，取较大值；如果是空头，currentPnLPct为负，也要比较）
		if currentPnLPct > peak {
			at.peakPnLCache[posKey] = currentPnLPct
		}
	} else {
		// 首次记录
		at.peakPnLCache[posKey] = currentPnLPct
	}
}

// ClearPeakPnLCache 清除指定持仓的峰值缓存
func (at *AutoTrader) ClearPeakPnLCache(symbol, side string) {
	at.peakPnLCacheMutex.Lock()
	defer at.peakPnLCacheMutex.Unlock()

	posKey := symbol + "_" + side
	delete(at.peakPnLCache, posKey)
}

// detectClosedPositions 检测被交易所自动平仓的持仓（止损/止盈触发）
// 对比上一次和当前的持仓快照，找出消失的持仓
func (at *AutoTrader) detectClosedPositions(currentPositions []decision.PositionInfo) []decision.PositionInfo {
	// 首次运行或没有缓存，返回空列表
	if len(at.lastPositions) == 0 {
		return []decision.PositionInfo{}
	}

	// 构建当前持仓的 key 集合
	currentKeys := make(map[string]bool)
	for _, pos := range currentPositions {
		key := pos.Symbol + "_" + pos.Side
		currentKeys[key] = true
	}

	// 检测消失的持仓
	var closedPositions []decision.PositionInfo
	for key, lastPos := range at.lastPositions {
		if !currentKeys[key] {
			// 持仓消失了，说明被自动平仓（止损/止盈触发）
			closedPositions = append(closedPositions, lastPos)
		}
	}

	return closedPositions
}

// generateAutoCloseActions 为被动平仓的持仓生成 DecisionAction
// generateAutoCloseActions - Create DecisionActions for passive closes with intelligent price/reason inference
func (at *AutoTrader) generateAutoCloseActions(closedPositions []decision.PositionInfo) []logger.DecisionAction {
	var actions []logger.DecisionAction

	for _, pos := range closedPositions {
		// 确定动作类型
		action := "auto_close_long"
		if pos.Side == "short" {
			action = "auto_close_short"
		}

		// 智能推断平仓价格和原因
		closePrice, closeReason := at.inferCloseDetails(pos)

		// 生成 DecisionAction
		actions = append(actions, logger.DecisionAction{
			Action:    action,
			Symbol:    pos.Symbol,
			Quantity:  pos.Quantity,
			Leverage:  pos.Leverage,
			Price:     closePrice, // 推断的平仓价格（止损/止盈/强平/市价）
			OrderID:   0,          // 自动平仓没有订单ID
			Timestamp: time.Now(), // 检测时间（非真实触发时间）
			Success:   true,
			Error:     closeReason, // 使用 Error 字段存储平仓原因（stop_loss/take_profit/liquidation/manual/unknown）
			Source:    pos.Source,
		})
	}

	return actions
}

// inferCloseDetails - Intelligently infer close price and reason based on position data
func (at *AutoTrader) inferCloseDetails(pos decision.PositionInfo) (price float64, reason string) {
	const priceThreshold = 0.01 // 1% 价格阈值，用于判断是否接近目标价格

	markPrice := pos.MarkPrice

	// 1. 优先检查是否接近强平价（爆仓）- 因为这是最严重的情况
	if pos.LiquidationPrice > 0 {
		liquidationThreshold := 0.02 // 2% 强平价阈值（更宽松，因为接近强平时会被系统平仓）
		if pos.Side == "long" {
			// 多头爆仓：价格接近强平价
			if markPrice <= pos.LiquidationPrice*(1+liquidationThreshold) {
				return pos.LiquidationPrice, "liquidation"
			}
		} else {
			// 空头爆仓：价格接近强平价
			if markPrice >= pos.LiquidationPrice*(1-liquidationThreshold) {
				return pos.LiquidationPrice, "liquidation"
			}
		}
	}

	// 2. 检查是否触发止损
	if pos.StopLoss > 0 {
		if pos.Side == "long" {
			// 多头止损：价格跌破止损价
			if markPrice <= pos.StopLoss*(1+priceThreshold) {
				return pos.StopLoss, "stop_loss"
			}
		} else {
			// 空头止损：价格涨破止损价
			if markPrice >= pos.StopLoss*(1-priceThreshold) {
				return pos.StopLoss, "stop_loss"
			}
		}
	}

	// 3. 检查是否触发止盈
	if pos.TakeProfit > 0 {
		if pos.Side == "long" {
			// 多头止盈：价格涨到止盈价
			if markPrice >= pos.TakeProfit*(1-priceThreshold) {
				return pos.TakeProfit, "take_profit"
			}
		} else {
			// 空头止盈：价格跌到止盈价
			if markPrice <= pos.TakeProfit*(1+priceThreshold) {
				return pos.TakeProfit, "take_profit"
			}
		}
	}

	// 4. 无法判断原因，可能是手动平仓或其他原因
	// 使用当前市场价作为估算平仓价
	return markPrice, "unknown"
}

// updatePositionSnapshot 更新持仓快照（在每次 buildTradingContext 后调用）
func (at *AutoTrader) updatePositionSnapshot(currentPositions []decision.PositionInfo) {
	// 清空旧快照
	at.lastPositions = make(map[string]decision.PositionInfo)

	// 保存当前持仓快照
	for _, pos := range currentPositions {
		key := pos.Symbol + "_" + pos.Side
		at.lastPositions[key] = pos
	}
}

// GetMartingaleStatus 获取马丁格尔策略状态
func (at *AutoTrader) GetMartingaleStatus() interface{} {
	return at.martingaleManager.GetPlans()
}

// DeleteMartingalePlan 删除指定马丁格尔计划
func (at *AutoTrader) DeleteMartingalePlan(planID string) error {
	if at.martingaleManager == nil {
		return fmt.Errorf("martingale manager not initialized")
	}
	return at.martingaleManager.DeletePlan(planID)
}

// runMartingaleLoop 独立运行马丁格尔策略的轮询循环 (30s)
func (at *AutoTrader) runMartingaleLoop() {
	at.monitorWg.Add(1)
	defer at.monitorWg.Done()

	ticker := time.NewTicker(30 * time.Second)
	defer ticker.Stop()

	log.Printf("🚀 马丁格尔独立轮询已启动 (周期: 30s)")

	for {
		select {
		case <-ticker.C:
			if !at.IsRunning() {
				return
			}

			// 创建临时记录对象
			record := &logger.DecisionRecord{
				Exchange:     at.config.Exchange,
				Timestamp:    time.Now(),
				ExecutionLog: []string{},
				Success:      true,
				Decisions:    []logger.DecisionAction{},
			}

			if err := at.runMartingaleTick(record); err != nil {
				log.Printf("⚠️ 马丁格尔轮询出错: %v", err)
			}

			// 只有当有决策生成时才保存日志
			if len(record.Decisions) > 0 {
				log.Printf("⚡ 马丁格尔触发 %d 个动作，正在保存日志...", len(record.Decisions))
				at.decisionLogger.LogDecision(record)
			}

		case <-at.stopMonitorCh:
			log.Printf("⏹ 马丁格尔轮询停止")
			return
		}
	}
}
